Commit aa24d1e9 authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Pavel Shilovsky

CIFS: Process reconnects for SMB2 shares

Signed-off-by: default avatarPavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent faaf946a
......@@ -171,6 +171,7 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data,
const char *devname);
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
extern void cifs_umount(struct cifs_sb_info *);
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
extern void cifs_dfs_release_automount_timer(void);
......
......@@ -112,24 +112,29 @@ cifs_kmap_unlock(void)
#define cifs_kmap_unlock() do { ; } while(0)
#endif /* CONFIG_HIGHMEM */
/* Mark as invalid, all open files on tree connections since they
were closed when session to server was lost */
static void mark_open_files_invalid(struct cifs_tcon *pTcon)
/*
* Mark as invalid, all open files on tree connections since they
* were closed when session to server was lost.
*/
void
cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
{
struct cifsFileInfo *open_file = NULL;
struct list_head *tmp;
struct list_head *tmp1;
/* list all files open on tree connection and mark them invalid */
/* list all files open on tree connection and mark them invalid */
spin_lock(&cifs_file_list_lock);
list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
open_file = list_entry(tmp, struct cifsFileInfo, tlist);
open_file->invalidHandle = true;
open_file->oplock_break_cancelled = true;
}
spin_unlock(&cifs_file_list_lock);
/* BB Add call to invalidate_inodes(sb) for all superblocks mounted
to this tcon */
/*
* BB Add call to invalidate_inodes(sb) for all superblocks mounted
* to this tcon.
*/
}
/* reconnect the socket, tcon, and smb session if needed */
......@@ -209,7 +214,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
goto out;
}
mark_open_files_invalid(tcon);
cifs_mark_open_files_invalid(tcon);
rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
mutex_unlock(&ses->session_mutex);
cFYI(1, "reconnect tcon rc = %d", rc);
......
......@@ -317,6 +317,9 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock);
server->maxBuf = 0;
#ifdef CONFIG_CIFS_SMB2
server->max_read = 0;
#endif
cFYI(1, "Reconnecting tcp session");
......
......@@ -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;
}
......
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