@@ -164,6 +164,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
164
164
struct nls_table * nls_codepage = NULL ;
165
165
struct cifs_ses * ses ;
166
166
int xid ;
167
+ struct TCP_Server_Info * pserver ;
168
+ unsigned int chan_index ;
167
169
168
170
/*
169
171
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
@@ -224,6 +226,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
224
226
return - EAGAIN ;
225
227
}
226
228
}
229
+
230
+ /* if server is marked for termination, cifsd will cleanup */
231
+ if (server -> terminate ) {
232
+ spin_unlock (& server -> srv_lock );
233
+ return - EHOSTDOWN ;
234
+ }
227
235
spin_unlock (& server -> srv_lock );
228
236
229
237
again :
@@ -242,12 +250,24 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
242
250
tcon -> need_reconnect );
243
251
244
252
mutex_lock (& ses -> session_mutex );
253
+ /*
254
+ * if this is called by delayed work, and the channel has been disabled
255
+ * in parallel, the delayed work can continue to execute in parallel
256
+ * there's a chance that this channel may not exist anymore
257
+ */
258
+ spin_lock (& server -> srv_lock );
259
+ if (server -> tcpStatus == CifsExiting ) {
260
+ spin_unlock (& server -> srv_lock );
261
+ mutex_unlock (& ses -> session_mutex );
262
+ rc = - EHOSTDOWN ;
263
+ goto out ;
264
+ }
265
+
245
266
/*
246
267
* Recheck after acquire mutex. If another thread is negotiating
247
268
* and the server never sends an answer the socket will be closed
248
269
* and tcpStatus set to reconnect.
249
270
*/
250
- spin_lock (& server -> srv_lock );
251
271
if (server -> tcpStatus == CifsNeedReconnect ) {
252
272
spin_unlock (& server -> srv_lock );
253
273
mutex_unlock (& ses -> session_mutex );
@@ -284,6 +304,53 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
284
304
285
305
rc = cifs_negotiate_protocol (0 , ses , server );
286
306
if (!rc ) {
307
+ /*
308
+ * if server stopped supporting multichannel
309
+ * and the first channel reconnected, disable all the others.
310
+ */
311
+ if (ses -> chan_count > 1 &&
312
+ !(server -> capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL )) {
313
+ if (SERVER_IS_CHAN (server )) {
314
+ cifs_dbg (VFS , "server %s does not support " \
315
+ "multichannel anymore. skipping secondary channel\n" ,
316
+ ses -> server -> hostname );
317
+
318
+ spin_lock (& ses -> chan_lock );
319
+ chan_index = cifs_ses_get_chan_index (ses , server );
320
+ if (chan_index == CIFS_INVAL_CHAN_INDEX ) {
321
+ spin_unlock (& ses -> chan_lock );
322
+ goto skip_terminate ;
323
+ }
324
+
325
+ ses -> chans [chan_index ].server = NULL ;
326
+ spin_unlock (& ses -> chan_lock );
327
+
328
+ /*
329
+ * the above reference of server by channel
330
+ * needs to be dropped without holding chan_lock
331
+ * as cifs_put_tcp_session takes a higher lock
332
+ * i.e. cifs_tcp_ses_lock
333
+ */
334
+ cifs_put_tcp_session (server , 1 );
335
+
336
+ server -> terminate = true;
337
+ cifs_signal_cifsd_for_reconnect (server , false);
338
+
339
+ /* mark primary server as needing reconnect */
340
+ pserver = server -> primary_server ;
341
+ cifs_signal_cifsd_for_reconnect (pserver , false);
342
+
343
+ skip_terminate :
344
+ mutex_unlock (& ses -> session_mutex );
345
+ rc = - EHOSTDOWN ;
346
+ goto out ;
347
+ } else {
348
+ cifs_server_dbg (VFS , "does not support " \
349
+ "multichannel anymore. disabling all other channels\n" );
350
+ cifs_disable_secondary_channels (ses );
351
+ }
352
+ }
353
+
287
354
rc = cifs_setup_session (0 , ses , server , nls_codepage );
288
355
if ((rc == - EACCES ) && !tcon -> retry ) {
289
356
mutex_unlock (& ses -> session_mutex );
@@ -3836,6 +3903,13 @@ void smb2_reconnect_server(struct work_struct *work)
3836
3903
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
3837
3904
mutex_lock (& pserver -> reconnect_mutex );
3838
3905
3906
+ /* if the server is marked for termination, drop the ref count here */
3907
+ if (server -> terminate ) {
3908
+ cifs_put_tcp_session (server , true);
3909
+ mutex_unlock (& pserver -> reconnect_mutex );
3910
+ return ;
3911
+ }
3912
+
3839
3913
INIT_LIST_HEAD (& tmp_list );
3840
3914
INIT_LIST_HEAD (& tmp_ses_list );
3841
3915
cifs_dbg (FYI , "Reconnecting tcons and channels\n" );
0 commit comments