@@ -289,14 +289,18 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
289
289
rc = - EHOSTDOWN ;
290
290
goto failed ;
291
291
}
292
- }
293
-
294
- if (rc || !tcon -> need_reconnect ) {
292
+ } else {
295
293
mutex_unlock (& ses -> session_mutex );
296
294
goto out ;
297
295
}
296
+ mutex_unlock (& ses -> session_mutex );
298
297
299
298
skip_sess_setup :
299
+ mutex_lock (& ses -> session_mutex );
300
+ if (!tcon -> need_reconnect ) {
301
+ mutex_unlock (& ses -> session_mutex );
302
+ goto out ;
303
+ }
300
304
cifs_mark_open_files_invalid (tcon );
301
305
if (tcon -> use_persistent )
302
306
tcon -> need_reopen_files = true;
@@ -3787,27 +3791,35 @@ void smb2_reconnect_server(struct work_struct *work)
3787
3791
{
3788
3792
struct TCP_Server_Info * server = container_of (work ,
3789
3793
struct TCP_Server_Info , reconnect .work );
3790
- struct cifs_ses * ses ;
3794
+ struct TCP_Server_Info * pserver ;
3795
+ struct cifs_ses * ses , * ses2 ;
3791
3796
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;
3794
3800
int rc ;
3795
- int resched = false;
3801
+ bool resched = false;
3796
3802
3803
+ /* If server is a channel, select the primary channel */
3804
+ pserver = CIFS_SERVER_IS_CHAN (server ) ? server -> primary_server : server ;
3797
3805
3798
3806
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
3799
- mutex_lock (& server -> reconnect_mutex );
3807
+ mutex_lock (& pserver -> reconnect_mutex );
3800
3808
3801
3809
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" );
3803
3812
3804
3813
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
+
3806
3818
list_for_each_entry (tcon , & ses -> tcon_list , tcon_list ) {
3807
3819
if (tcon -> need_reconnect || tcon -> need_reopen_files ) {
3808
3820
tcon -> tc_count ++ ;
3809
3821
list_add_tail (& tcon -> rlist , & tmp_list );
3810
- tcon_exist = true;
3822
+ tcon_selected = tcon_exist = true;
3811
3823
}
3812
3824
}
3813
3825
/*
@@ -3816,15 +3828,25 @@ void smb2_reconnect_server(struct work_struct *work)
3816
3828
*/
3817
3829
if (ses -> tcon_ipc && ses -> tcon_ipc -> need_reconnect ) {
3818
3830
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;
3820
3842
ses -> ses_count ++ ;
3821
3843
}
3822
3844
}
3823
3845
/*
3824
3846
* Get the reference to server struct to be sure that the last call of
3825
3847
* cifs_put_tcon() in the loop below won't release the server pointer.
3826
3848
*/
3827
- if (tcon_exist )
3849
+ if (tcon_exist || ses_exist )
3828
3850
server -> srv_count ++ ;
3829
3851
3830
3852
spin_unlock (& cifs_tcp_ses_lock );
@@ -3842,13 +3864,41 @@ void smb2_reconnect_server(struct work_struct *work)
3842
3864
cifs_put_tcon (tcon );
3843
3865
}
3844
3866
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" );
3846
3896
if (resched )
3847
3897
queue_delayed_work (cifsiod_wq , & server -> reconnect , 2 * HZ );
3848
- mutex_unlock (& server -> reconnect_mutex );
3898
+ mutex_unlock (& pserver -> reconnect_mutex );
3849
3899
3850
3900
/* now we can safely release srv struct */
3851
- if (tcon_exist )
3901
+ if (tcon_exist || ses_exist )
3852
3902
cifs_put_tcp_session (server , 1 );
3853
3903
}
3854
3904
0 commit comments