CIFS: Process reconnects for SMB2 shares
authorPavel Shilovsky <piastry@etersoft.ru>
Tue, 27 Dec 2011 12:23:34 +0000 (16:23 +0400)
committerPavel Shilovsky <pshilovsky@samba.org>
Tue, 24 Jul 2012 17:54:59 +0000 (21:54 +0400)
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/smb2pdu.c

index 3b4d41f9ceebb30c4daa7f45cdab6925e9e0330c..61baaa3330fbd9309a5b11cbcf9a35dfca8c6b7f 100644 (file)
@@ -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);
index dcb0ad87e1739fd6dbe5808b7266f6ce9254001f..f1dfc7844f1be755784a2ed48ae342a09c7943cb 100644 (file)
@@ -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);
index a6197224b102699a20607a88a5da6c9a5f269cd6..7cf8b163224237da705997a544abd6e87b63e7e8 100644 (file)
@@ -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");
 
index 1bf037ec5a9d5e31541455271a48ff9ab1a5b7c4..48c04b2832e26fbda75adcef334a5d894e08e502 100644 (file)
@@ -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;
 }