diff options
author | Pavel Shilovsky <piastry@etersoft.ru> | 2011-12-27 07:23:34 -0500 |
---|---|---|
committer | Pavel Shilovsky <pshilovsky@samba.org> | 2012-07-24 13:54:59 -0400 |
commit | aa24d1e9692411e605084938ced6b160f92df454 (patch) | |
tree | 399446513c2437af67ca8ed065ba16f272715bb4 /fs | |
parent | faaf946a7d5b79194358437150f34ab4c66bfe21 (diff) |
CIFS: Process reconnects for SMB2 shares
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifsproto.h | 1 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 21 | ||||
-rw-r--r-- | fs/cifs/connect.c | 3 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 127 |
4 files changed, 143 insertions, 9 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 3b4d41f9ceeb..61baaa3330fb 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -171,6 +171,7 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data, | |||
171 | const char *devname); | 171 | const char *devname); |
172 | extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); | 172 | extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); |
173 | extern void cifs_umount(struct cifs_sb_info *); | 173 | extern void cifs_umount(struct cifs_sb_info *); |
174 | extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); | ||
174 | 175 | ||
175 | #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) | 176 | #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) |
176 | extern void cifs_dfs_release_automount_timer(void); | 177 | extern void cifs_dfs_release_automount_timer(void); |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index dcb0ad87e173..f1dfc7844f1b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -112,24 +112,29 @@ cifs_kmap_unlock(void) | |||
112 | #define cifs_kmap_unlock() do { ; } while(0) | 112 | #define cifs_kmap_unlock() do { ; } while(0) |
113 | #endif /* CONFIG_HIGHMEM */ | 113 | #endif /* CONFIG_HIGHMEM */ |
114 | 114 | ||
115 | /* Mark as invalid, all open files on tree connections since they | 115 | /* |
116 | were closed when session to server was lost */ | 116 | * Mark as invalid, all open files on tree connections since they |
117 | static void mark_open_files_invalid(struct cifs_tcon *pTcon) | 117 | * were closed when session to server was lost. |
118 | */ | ||
119 | void | ||
120 | cifs_mark_open_files_invalid(struct cifs_tcon *tcon) | ||
118 | { | 121 | { |
119 | struct cifsFileInfo *open_file = NULL; | 122 | struct cifsFileInfo *open_file = NULL; |
120 | struct list_head *tmp; | 123 | struct list_head *tmp; |
121 | struct list_head *tmp1; | 124 | struct list_head *tmp1; |
122 | 125 | ||
123 | /* list all files open on tree connection and mark them invalid */ | 126 | /* list all files open on tree connection and mark them invalid */ |
124 | spin_lock(&cifs_file_list_lock); | 127 | spin_lock(&cifs_file_list_lock); |
125 | list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { | 128 | list_for_each_safe(tmp, tmp1, &tcon->openFileList) { |
126 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); | 129 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); |
127 | open_file->invalidHandle = true; | 130 | open_file->invalidHandle = true; |
128 | open_file->oplock_break_cancelled = true; | 131 | open_file->oplock_break_cancelled = true; |
129 | } | 132 | } |
130 | spin_unlock(&cifs_file_list_lock); | 133 | spin_unlock(&cifs_file_list_lock); |
131 | /* BB Add call to invalidate_inodes(sb) for all superblocks mounted | 134 | /* |
132 | to this tcon */ | 135 | * BB Add call to invalidate_inodes(sb) for all superblocks mounted |
136 | * to this tcon. | ||
137 | */ | ||
133 | } | 138 | } |
134 | 139 | ||
135 | /* reconnect the socket, tcon, and smb session if needed */ | 140 | /* reconnect the socket, tcon, and smb session if needed */ |
@@ -209,7 +214,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) | |||
209 | goto out; | 214 | goto out; |
210 | } | 215 | } |
211 | 216 | ||
212 | mark_open_files_invalid(tcon); | 217 | cifs_mark_open_files_invalid(tcon); |
213 | rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage); | 218 | rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage); |
214 | mutex_unlock(&ses->session_mutex); | 219 | mutex_unlock(&ses->session_mutex); |
215 | cFYI(1, "reconnect tcon rc = %d", rc); | 220 | cFYI(1, "reconnect tcon rc = %d", rc); |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a6197224b102..7cf8b1632242 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -317,6 +317,9 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
317 | server->tcpStatus = CifsNeedReconnect; | 317 | server->tcpStatus = CifsNeedReconnect; |
318 | spin_unlock(&GlobalMid_Lock); | 318 | spin_unlock(&GlobalMid_Lock); |
319 | server->maxBuf = 0; | 319 | server->maxBuf = 0; |
320 | #ifdef CONFIG_CIFS_SMB2 | ||
321 | server->max_read = 0; | ||
322 | #endif | ||
320 | 323 | ||
321 | cFYI(1, "Reconnecting tcp session"); | 324 | cFYI(1, "Reconnecting tcp session"); |
322 | 325 | ||
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 1bf037ec5a9d..48c04b2832e2 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -127,7 +127,132 @@ static int | |||
127 | smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | 127 | smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) |
128 | { | 128 | { |
129 | int rc = 0; | 129 | int rc = 0; |
130 | /* BB add missing code here */ | 130 | struct nls_table *nls_codepage; |
131 | struct cifs_ses *ses; | ||
132 | struct TCP_Server_Info *server; | ||
133 | |||
134 | /* | ||
135 | * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so | ||
136 | * check for tcp and smb session status done differently | ||
137 | * for those three - in the calling routine. | ||
138 | */ | ||
139 | if (tcon == NULL) | ||
140 | return rc; | ||
141 | |||
142 | if (smb2_command == SMB2_TREE_CONNECT) | ||
143 | return rc; | ||
144 | |||
145 | if (tcon->tidStatus == CifsExiting) { | ||
146 | /* | ||
147 | * only tree disconnect, open, and write, | ||
148 | * (and ulogoff which does not have tcon) | ||
149 | * are allowed as we start force umount. | ||
150 | */ | ||
151 | if ((smb2_command != SMB2_WRITE) && | ||
152 | (smb2_command != SMB2_CREATE) && | ||
153 | (smb2_command != SMB2_TREE_DISCONNECT)) { | ||
154 | cFYI(1, "can not send cmd %d while umounting", | ||
155 | smb2_command); | ||
156 | return -ENODEV; | ||
157 | } | ||
158 | } | ||
159 | if ((!tcon->ses) || (tcon->ses->status == CifsExiting) || | ||
160 | (!tcon->ses->server)) | ||
161 | return -EIO; | ||
162 | |||
163 | ses = tcon->ses; | ||
164 | server = ses->server; | ||
165 | |||
166 | /* | ||
167 | * Give demultiplex thread up to 10 seconds to reconnect, should be | ||
168 | * greater than cifs socket timeout which is 7 seconds | ||
169 | */ | ||
170 | while (server->tcpStatus == CifsNeedReconnect) { | ||
171 | /* | ||
172 | * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE | ||
173 | * here since they are implicitly done when session drops. | ||
174 | */ | ||
175 | switch (smb2_command) { | ||
176 | /* | ||
177 | * BB Should we keep oplock break and add flush to exceptions? | ||
178 | */ | ||
179 | case SMB2_TREE_DISCONNECT: | ||
180 | case SMB2_CANCEL: | ||
181 | case SMB2_CLOSE: | ||
182 | case SMB2_OPLOCK_BREAK: | ||
183 | return -EAGAIN; | ||
184 | } | ||
185 | |||
186 | wait_event_interruptible_timeout(server->response_q, | ||
187 | (server->tcpStatus != CifsNeedReconnect), 10 * HZ); | ||
188 | |||
189 | /* are we still trying to reconnect? */ | ||
190 | if (server->tcpStatus != CifsNeedReconnect) | ||
191 | break; | ||
192 | |||
193 | /* | ||
194 | * on "soft" mounts we wait once. Hard mounts keep | ||
195 | * retrying until process is killed or server comes | ||
196 | * back on-line | ||
197 | */ | ||
198 | if (!tcon->retry) { | ||
199 | cFYI(1, "gave up waiting on reconnect in smb_init"); | ||
200 | return -EHOSTDOWN; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | if (!tcon->ses->need_reconnect && !tcon->need_reconnect) | ||
205 | return rc; | ||
206 | |||
207 | nls_codepage = load_nls_default(); | ||
208 | |||
209 | /* | ||
210 | * need to prevent multiple threads trying to simultaneously reconnect | ||
211 | * the same SMB session | ||
212 | */ | ||
213 | mutex_lock(&tcon->ses->session_mutex); | ||
214 | rc = cifs_negotiate_protocol(0, tcon->ses); | ||
215 | if (!rc && tcon->ses->need_reconnect) | ||
216 | rc = cifs_setup_session(0, tcon->ses, nls_codepage); | ||
217 | |||
218 | if (rc || !tcon->need_reconnect) { | ||
219 | mutex_unlock(&tcon->ses->session_mutex); | ||
220 | goto out; | ||
221 | } | ||
222 | |||
223 | cifs_mark_open_files_invalid(tcon); | ||
224 | rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); | ||
225 | mutex_unlock(&tcon->ses->session_mutex); | ||
226 | cFYI(1, "reconnect tcon rc = %d", rc); | ||
227 | if (rc) | ||
228 | goto out; | ||
229 | atomic_inc(&tconInfoReconnectCount); | ||
230 | /* | ||
231 | * BB FIXME add code to check if wsize needs update due to negotiated | ||
232 | * smb buffer size shrinking. | ||
233 | */ | ||
234 | out: | ||
235 | /* | ||
236 | * Check if handle based operation so we know whether we can continue | ||
237 | * or not without returning to caller to reset file handle. | ||
238 | */ | ||
239 | /* | ||
240 | * BB Is flush done by server on drop of tcp session? Should we special | ||
241 | * case it and skip above? | ||
242 | */ | ||
243 | switch (smb2_command) { | ||
244 | case SMB2_FLUSH: | ||
245 | case SMB2_READ: | ||
246 | case SMB2_WRITE: | ||
247 | case SMB2_LOCK: | ||
248 | case SMB2_IOCTL: | ||
249 | case SMB2_QUERY_DIRECTORY: | ||
250 | case SMB2_CHANGE_NOTIFY: | ||
251 | case SMB2_QUERY_INFO: | ||
252 | case SMB2_SET_INFO: | ||
253 | return -EAGAIN; | ||
254 | } | ||
255 | unload_nls(nls_codepage); | ||
131 | return rc; | 256 | return rc; |
132 | } | 257 | } |
133 | 258 | ||