@@ -289,14 +289,18 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
289289 rc = - EHOSTDOWN ;
290290 goto failed ;
291291 }
292- }
293-
294- if (rc || !tcon -> need_reconnect ) {
292+ } else {
295293 mutex_unlock (& ses -> session_mutex );
296294 goto out ;
297295 }
296+ mutex_unlock (& ses -> session_mutex );
298297
299298skip_sess_setup :
299+ mutex_lock (& ses -> session_mutex );
300+ if (!tcon -> need_reconnect ) {
301+ mutex_unlock (& ses -> session_mutex );
302+ goto out ;
303+ }
300304 cifs_mark_open_files_invalid (tcon );
301305 if (tcon -> use_persistent )
302306 tcon -> need_reopen_files = true;
@@ -3787,27 +3791,35 @@ void smb2_reconnect_server(struct work_struct *work)
37873791{
37883792 struct TCP_Server_Info * server = container_of (work ,
37893793 struct TCP_Server_Info , reconnect .work );
3790- struct cifs_ses * ses ;
3794+ struct TCP_Server_Info * pserver ;
3795+ struct cifs_ses * ses , * ses2 ;
37913796 struct cifs_tcon * tcon , * tcon2 ;
3792- struct list_head tmp_list ;
3793- int tcon_exist = false;
3797+ struct list_head tmp_list , tmp_ses_list ;
3798+ bool tcon_exist = false, ses_exist = false;
3799+ bool tcon_selected = false, ses_selected = false;
37943800 int rc ;
3795- int resched = false;
3801+ bool resched = false;
37963802
3803+ /* If server is a channel, select the primary channel */
3804+ pserver = CIFS_SERVER_IS_CHAN (server ) ? server -> primary_server : server ;
37973805
37983806 /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
3799- mutex_lock (& server -> reconnect_mutex );
3807+ mutex_lock (& pserver -> reconnect_mutex );
38003808
38013809 INIT_LIST_HEAD (& tmp_list );
3802- cifs_dbg (FYI , "Need negotiate, reconnecting tcons\n" );
3810+ INIT_LIST_HEAD (& tmp_ses_list );
3811+ cifs_dbg (FYI , "Reconnecting tcons and channels\n" );
38033812
38043813 spin_lock (& cifs_tcp_ses_lock );
3805- list_for_each_entry (ses , & server -> smb_ses_list , smb_ses_list ) {
3814+ list_for_each_entry (ses , & pserver -> smb_ses_list , smb_ses_list ) {
3815+
3816+ tcon_selected = ses_selected = false;
3817+
38063818 list_for_each_entry (tcon , & ses -> tcon_list , tcon_list ) {
38073819 if (tcon -> need_reconnect || tcon -> need_reopen_files ) {
38083820 tcon -> tc_count ++ ;
38093821 list_add_tail (& tcon -> rlist , & tmp_list );
3810- tcon_exist = true;
3822+ tcon_selected = tcon_exist = true;
38113823 }
38123824 }
38133825 /*
@@ -3816,15 +3828,25 @@ void smb2_reconnect_server(struct work_struct *work)
38163828 */
38173829 if (ses -> tcon_ipc && ses -> tcon_ipc -> need_reconnect ) {
38183830 list_add_tail (& ses -> tcon_ipc -> rlist , & tmp_list );
3819- tcon_exist = true;
3831+ tcon_selected = tcon_exist = true;
3832+ ses -> ses_count ++ ;
3833+ }
3834+ /*
3835+ * handle the case where channel needs to reconnect
3836+ * binding session, but tcon is healthy (some other channel
3837+ * is active)
3838+ */
3839+ if (!tcon_selected && cifs_chan_needs_reconnect (ses , server )) {
3840+ list_add_tail (& ses -> rlist , & tmp_ses_list );
3841+ ses_selected = ses_exist = true;
38203842 ses -> ses_count ++ ;
38213843 }
38223844 }
38233845 /*
38243846 * Get the reference to server struct to be sure that the last call of
38253847 * cifs_put_tcon() in the loop below won't release the server pointer.
38263848 */
3827- if (tcon_exist )
3849+ if (tcon_exist || ses_exist )
38283850 server -> srv_count ++ ;
38293851
38303852 spin_unlock (& cifs_tcp_ses_lock );
@@ -3842,13 +3864,41 @@ void smb2_reconnect_server(struct work_struct *work)
38423864 cifs_put_tcon (tcon );
38433865 }
38443866
3845- cifs_dbg (FYI , "Reconnecting tcons finished\n" );
3867+ if (!ses_exist )
3868+ goto done ;
3869+
3870+ /* allocate a dummy tcon struct used for reconnect */
3871+ tcon = kzalloc (sizeof (struct cifs_tcon ), GFP_KERNEL );
3872+ if (!tcon ) {
3873+ resched = true;
3874+ list_del_init (& ses -> rlist );
3875+ cifs_put_smb_ses (ses );
3876+ goto done ;
3877+ }
3878+
3879+ tcon -> tidStatus = CifsGood ;
3880+ tcon -> retry = false;
3881+ tcon -> need_reconnect = false;
3882+
3883+ /* now reconnect sessions for necessary channels */
3884+ list_for_each_entry_safe (ses , ses2 , & tmp_ses_list , rlist ) {
3885+ tcon -> ses = ses ;
3886+ rc = smb2_reconnect (SMB2_INTERNAL_CMD , tcon , server );
3887+ if (rc )
3888+ resched = true;
3889+ list_del_init (& ses -> rlist );
3890+ cifs_put_smb_ses (ses );
3891+ }
3892+ kfree (tcon );
3893+
3894+ done :
3895+ cifs_dbg (FYI , "Reconnecting tcons and channels finished\n" );
38463896 if (resched )
38473897 queue_delayed_work (cifsiod_wq , & server -> reconnect , 2 * HZ );
3848- mutex_unlock (& server -> reconnect_mutex );
3898+ mutex_unlock (& pserver -> reconnect_mutex );
38493899
38503900 /* now we can safely release srv struct */
3851- if (tcon_exist )
3901+ if (tcon_exist || ses_exist )
38523902 cifs_put_tcp_session (server , 1 );
38533903}
38543904
0 commit comments