@@ -164,6 +164,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
164164 struct nls_table * nls_codepage = NULL ;
165165 struct cifs_ses * ses ;
166166 int xid ;
167+ struct TCP_Server_Info * pserver ;
168+ unsigned int chan_index ;
167169
168170 /*
169171 * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
@@ -224,6 +226,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
224226 return - EAGAIN ;
225227 }
226228 }
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+ }
227235 spin_unlock (& server -> srv_lock );
228236
229237again :
@@ -242,12 +250,24 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
242250 tcon -> need_reconnect );
243251
244252 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+
245266 /*
246267 * Recheck after acquire mutex. If another thread is negotiating
247268 * and the server never sends an answer the socket will be closed
248269 * and tcpStatus set to reconnect.
249270 */
250- spin_lock (& server -> srv_lock );
251271 if (server -> tcpStatus == CifsNeedReconnect ) {
252272 spin_unlock (& server -> srv_lock );
253273 mutex_unlock (& ses -> session_mutex );
@@ -284,6 +304,53 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
284304
285305 rc = cifs_negotiate_protocol (0 , ses , server );
286306 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+
287354 rc = cifs_setup_session (0 , ses , server , nls_codepage );
288355 if ((rc == - EACCES ) && !tcon -> retry ) {
289356 mutex_unlock (& ses -> session_mutex );
@@ -3836,6 +3903,13 @@ void smb2_reconnect_server(struct work_struct *work)
38363903 /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
38373904 mutex_lock (& pserver -> reconnect_mutex );
38383905
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+
38393913 INIT_LIST_HEAD (& tmp_list );
38403914 INIT_LIST_HEAD (& tmp_ses_list );
38413915 cifs_dbg (FYI , "Reconnecting tcons and channels\n" );
0 commit comments