diff options
Diffstat (limited to 'fs/cifs/smb2pdu.c')
-rw-r--r-- | fs/cifs/smb2pdu.c | 127 |
1 files changed, 126 insertions, 1 deletions
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 1bf037e..48c04b2 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -127,7 +127,132 @@ static int smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) { int rc = 0; - /* BB add missing code here */ + struct nls_table *nls_codepage; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + + /* + * 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 rc; + + if (smb2_command == SMB2_TREE_CONNECT) + return rc; + + 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)) { + cFYI(1, "can not send cmd %d while umounting", + smb2_command); + return -ENODEV; + } + } + if ((!tcon->ses) || (tcon->ses->status == CifsExiting) || + (!tcon->ses->server)) + return -EIO; + + ses = tcon->ses; + server = ses->server; + + /* + * Give demultiplex thread up to 10 seconds to 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; + } + + wait_event_interruptible_timeout(server->response_q, + (server->tcpStatus != CifsNeedReconnect), 10 * HZ); + + /* are we still trying to reconnect? */ + if (server->tcpStatus != CifsNeedReconnect) + break; + + /* + * on "soft" mounts we wait once. Hard mounts keep + * retrying until process is killed or server comes + * back on-line + */ + if (!tcon->retry) { + cFYI(1, "gave up waiting on reconnect in smb_init"); + return -EHOSTDOWN; + } + } + + if (!tcon->ses->need_reconnect && !tcon->need_reconnect) + return rc; + + nls_codepage = load_nls_default(); + + /* + * need to prevent multiple threads trying to simultaneously reconnect + * the same SMB session + */ + mutex_lock(&tcon->ses->session_mutex); + rc = cifs_negotiate_protocol(0, tcon->ses); + if (!rc && tcon->ses->need_reconnect) + rc = cifs_setup_session(0, tcon->ses, nls_codepage); + + if (rc || !tcon->need_reconnect) { + mutex_unlock(&tcon->ses->session_mutex); + goto out; + } + + cifs_mark_open_files_invalid(tcon); + rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); + mutex_unlock(&tcon->ses->session_mutex); + cFYI(1, "reconnect tcon rc = %d", rc); + if (rc) + goto out; + atomic_inc(&tconInfoReconnectCount); + /* + * BB FIXME add code to check if wsize needs update due to negotiated + * smb buffer size shrinking. + */ +out: + /* + * Check if handle based operation so we know whether we can continue + * or not without returning to caller to reset file handle. + */ + /* + * BB Is flush done by server on drop of tcp session? Should we special + * case it and skip above? + */ + switch (smb2_command) { + case SMB2_FLUSH: + case SMB2_READ: + case SMB2_WRITE: + case SMB2_LOCK: + case SMB2_IOCTL: + case SMB2_QUERY_DIRECTORY: + case SMB2_CHANGE_NOTIFY: + case SMB2_QUERY_INFO: + case SMB2_SET_INFO: + return -EAGAIN; + } + unload_nls(nls_codepage); return rc; } |