@@ -148,57 +148,6 @@ static void cifs_resolve_server(struct work_struct *work)
148148 mutex_unlock (& server -> srv_mutex );
149149}
150150
151- #ifdef CONFIG_CIFS_DFS_UPCALL
152- /* These functions must be called with server->srv_mutex held */
153- static void reconn_set_next_dfs_target (struct TCP_Server_Info * server ,
154- struct cifs_sb_info * cifs_sb ,
155- struct dfs_cache_tgt_list * tgt_list ,
156- struct dfs_cache_tgt_iterator * * tgt_it )
157- {
158- const char * name ;
159- int rc ;
160-
161- if (!cifs_sb || !cifs_sb -> origin_fullpath )
162- return ;
163-
164- if (!* tgt_it ) {
165- * tgt_it = dfs_cache_get_tgt_iterator (tgt_list );
166- } else {
167- * tgt_it = dfs_cache_get_next_tgt (tgt_list , * tgt_it );
168- if (!* tgt_it )
169- * tgt_it = dfs_cache_get_tgt_iterator (tgt_list );
170- }
171-
172- cifs_dbg (FYI , "%s: UNC: %s\n" , __func__ , cifs_sb -> origin_fullpath );
173-
174- name = dfs_cache_get_tgt_name (* tgt_it );
175-
176- kfree (server -> hostname );
177-
178- server -> hostname = extract_hostname (name );
179- if (IS_ERR (server -> hostname )) {
180- cifs_dbg (FYI ,
181- "%s: failed to extract hostname from target: %ld\n" ,
182- __func__ , PTR_ERR (server -> hostname ));
183- return ;
184- }
185-
186- rc = reconn_set_ipaddr_from_hostname (server );
187- if (rc ) {
188- cifs_dbg (FYI , "%s: failed to resolve hostname: %d\n" ,
189- __func__ , rc );
190- }
191- }
192-
193- static inline int reconn_setup_dfs_targets (struct cifs_sb_info * cifs_sb ,
194- struct dfs_cache_tgt_list * tl )
195- {
196- if (!cifs_sb -> origin_fullpath )
197- return - EOPNOTSUPP ;
198- return dfs_cache_noreq_find (cifs_sb -> origin_fullpath + 1 , NULL , tl );
199- }
200- #endif
201-
202151/**
203152 * Mark all sessions and tcons for reconnect.
204153 *
@@ -278,6 +227,21 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
278227 }
279228}
280229
230+ static bool cifs_tcp_ses_needs_reconnect (struct TCP_Server_Info * server , int num_targets )
231+ {
232+ spin_lock (& GlobalMid_Lock );
233+ server -> nr_targets = num_targets ;
234+ if (server -> tcpStatus == CifsExiting ) {
235+ /* the demux thread will exit normally next time through the loop */
236+ spin_unlock (& GlobalMid_Lock );
237+ wake_up (& server -> response_q );
238+ return false;
239+ }
240+ server -> tcpStatus = CifsNeedReconnect ;
241+ spin_unlock (& GlobalMid_Lock );
242+ return true;
243+ }
244+
281245/*
282246 * cifs tcp session reconnection
283247 *
@@ -286,99 +250,32 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
286250 * reconnect tcp session
287251 * wake up waiters on reconnection? - (not needed currently)
288252 */
289- int
290- cifs_reconnect (struct TCP_Server_Info * server )
253+ static int __cifs_reconnect (struct TCP_Server_Info * server )
291254{
292255 int rc = 0 ;
293- #ifdef CONFIG_CIFS_DFS_UPCALL
294- struct super_block * sb = NULL ;
295- struct cifs_sb_info * cifs_sb = NULL ;
296- struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT (tgt_list );
297- struct dfs_cache_tgt_iterator * tgt_it = NULL ;
298- #endif
299256
300- spin_lock (& GlobalMid_Lock );
301- server -> nr_targets = 1 ;
302- #ifdef CONFIG_CIFS_DFS_UPCALL
303- spin_unlock (& GlobalMid_Lock );
304- sb = cifs_get_tcp_super (server );
305- if (IS_ERR (sb )) {
306- rc = PTR_ERR (sb );
307- cifs_dbg (FYI , "%s: will not do DFS failover: rc = %d\n" ,
308- __func__ , rc );
309- sb = NULL ;
310- } else {
311- cifs_sb = CIFS_SB (sb );
312- rc = reconn_setup_dfs_targets (cifs_sb , & tgt_list );
313- if (rc ) {
314- cifs_sb = NULL ;
315- if (rc != - EOPNOTSUPP ) {
316- cifs_server_dbg (VFS , "%s: no target servers for DFS failover\n" ,
317- __func__ );
318- }
319- } else {
320- server -> nr_targets = dfs_cache_get_nr_tgts (& tgt_list );
321- }
322- }
323- cifs_dbg (FYI , "%s: will retry %d target(s)\n" , __func__ ,
324- server -> nr_targets );
325- spin_lock (& GlobalMid_Lock );
326- #endif
327- if (server -> tcpStatus == CifsExiting ) {
328- /* the demux thread will exit normally next time through the loop */
329- spin_unlock (& GlobalMid_Lock );
330- #ifdef CONFIG_CIFS_DFS_UPCALL
331- dfs_cache_free_tgts (& tgt_list );
332- cifs_put_tcp_super (sb );
333- #endif
334- wake_up (& server -> response_q );
335- return rc ;
336- } else
337- server -> tcpStatus = CifsNeedReconnect ;
338- spin_unlock (& GlobalMid_Lock );
257+ if (!cifs_tcp_ses_needs_reconnect (server , 1 ))
258+ return 0 ;
339259
340260 cifs_mark_tcp_ses_conns_for_reconnect (server );
341261
342262 do {
343263 try_to_freeze ();
344-
345264 mutex_lock (& server -> srv_mutex );
346265
347-
348266 if (!cifs_swn_set_server_dstaddr (server )) {
349- #ifdef CONFIG_CIFS_DFS_UPCALL
350- if (cifs_sb && cifs_sb -> origin_fullpath )
351- /*
352- * Set up next DFS target server (if any) for reconnect. If DFS
353- * feature is disabled, then we will retry last server we
354- * connected to before.
355- */
356- reconn_set_next_dfs_target (server , cifs_sb , & tgt_list , & tgt_it );
357- else {
358- #endif
359- /*
360- * Resolve the hostname again to make sure that IP address is up-to-date.
361- */
267+ /* resolve the hostname again to make sure that IP address is up-to-date */
362268 rc = reconn_set_ipaddr_from_hostname (server );
363- if (rc ) {
364- cifs_dbg (FYI , "%s: failed to resolve hostname: %d\n" ,
365- __func__ , rc );
366- }
367-
368- #ifdef CONFIG_CIFS_DFS_UPCALL
369- }
370- #endif
371-
372-
269+ cifs_dbg (FYI , "%s: reconn_set_ipaddr_from_hostname: rc=%d\n" , __func__ , rc );
373270 }
374271
375272 if (cifs_rdma_enabled (server ))
376273 rc = smbd_reconnect (server );
377274 else
378275 rc = generic_ip_connect (server );
379276 if (rc ) {
380- cifs_dbg (FYI , "reconnect error %d\n" , rc );
381277 mutex_unlock (& server -> srv_mutex );
278+ cifs_dbg (FYI , "%s: reconnect error %d\n" , __func__ , rc );
382279 msleep (3000 );
383280 } else {
384281 atomic_inc (& tcpSesReconnectCount );
@@ -392,26 +289,158 @@ cifs_reconnect(struct TCP_Server_Info *server)
392289 }
393290 } while (server -> tcpStatus == CifsNeedReconnect );
394291
292+ if (server -> tcpStatus == CifsNeedNegotiate )
293+ mod_delayed_work (cifsiod_wq , & server -> echo , 0 );
294+
295+ wake_up (& server -> response_q );
296+ return rc ;
297+ }
298+
395299#ifdef CONFIG_CIFS_DFS_UPCALL
396- if (tgt_it ) {
397- rc = dfs_cache_noreq_update_tgthint (cifs_sb -> origin_fullpath + 1 ,
398- tgt_it );
300+ static int reconnect_dfs_server (struct TCP_Server_Info * server , struct cifs_sb_info * cifs_sb )
301+ {
302+ int rc = 0 ;
303+ const char * refpath = cifs_sb -> origin_fullpath + 1 ;
304+ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT (tl );
305+ struct dfs_cache_tgt_iterator * tit = NULL ;
306+ int num_targets = 1 ;
307+ char * hostname ;
308+
309+ /*
310+ * Determine the number of dfs targets the referral path in @cifs_sb resolves to.
311+ *
312+ * smb2_reconnect() needs to know how long it should wait based upon the number of dfs
313+ * targets (server->nr_targets). It's also possible that the cached referral was cleared
314+ * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
315+ * refreshing the referral, so, in this case, default it to 1.
316+ */
317+ if (!dfs_cache_noreq_find (refpath , NULL , & tl )) {
318+ num_targets = dfs_cache_get_nr_tgts (& tl );
319+ if (!num_targets )
320+ num_targets = 1 ;
321+ }
322+
323+ if (!cifs_tcp_ses_needs_reconnect (server , num_targets ))
324+ return 0 ;
325+
326+ cifs_mark_tcp_ses_conns_for_reconnect (server );
327+
328+ do {
329+ /* Get next dfs target from target list (if any) */
330+ if (!tit )
331+ tit = dfs_cache_get_tgt_iterator (& tl );
332+ else
333+ tit = dfs_cache_get_next_tgt (& tl , tit );
334+
335+ try_to_freeze ();
336+ mutex_lock (& server -> srv_mutex );
337+
338+ if (!cifs_swn_set_server_dstaddr (server )) {
339+ /*
340+ * If any dfs target was selected, then update @server with either a
341+ * hostname or an address.
342+ */
343+ if (tit ) {
344+ hostname = extract_hostname (dfs_cache_get_tgt_name (tit ));
345+ if (!IS_ERR (hostname )) {
346+ kfree (server -> hostname );
347+ server -> hostname = hostname ;
348+ } else {
349+ cifs_dbg (FYI , "%s: couldn't extract hostname or address from dfs target: %ld\n" ,
350+ __func__ , PTR_ERR (hostname ));
351+ cifs_dbg (FYI , "%s: default to last target server: %s\n" ,
352+ __func__ , server -> hostname );
353+ }
354+ }
355+ /* resolve the hostname again to make sure that IP address is up-to-date. */
356+ rc = reconn_set_ipaddr_from_hostname (server );
357+ cifs_dbg (FYI , "%s: reconn_set_ipaddr_from_hostname: rc=%d\n" , __func__ , rc );
358+ }
359+
360+ /* Reconnect the socket */
361+ if (cifs_rdma_enabled (server ))
362+ rc = smbd_reconnect (server );
363+ else
364+ rc = generic_ip_connect (server );
365+
399366 if (rc ) {
400- cifs_server_dbg (VFS , "%s: failed to update DFS target hint: rc = %d\n" ,
401- __func__ , rc );
367+ /* Failed to reconnect socket. Retry next dfs target. */
368+ mutex_unlock (& server -> srv_mutex );
369+ cifs_dbg (FYI , "%s: reconnect error %d\n" , __func__ , rc );
370+ msleep (3000 );
371+ continue ;
402372 }
403- dfs_cache_free_tgts (& tgt_list );
404- }
405373
406- cifs_put_tcp_super (sb );
407- #endif
374+ /*
375+ * Socket was created. Update tcp session status to CifsNeedNegotiate so that a
376+ * process waiting for reconnect will know it needs to re-establish session and tcon
377+ * through the reconnected target server.
378+ */
379+ atomic_inc (& tcpSesReconnectCount );
380+ set_credits (server , 1 );
381+ spin_lock (& GlobalMid_Lock );
382+ if (server -> tcpStatus != CifsExiting )
383+ server -> tcpStatus = CifsNeedNegotiate ;
384+ spin_unlock (& GlobalMid_Lock );
385+ cifs_swn_reset_server_dstaddr (server );
386+ mutex_unlock (& server -> srv_mutex );
387+ } while (server -> tcpStatus == CifsNeedReconnect );
388+
389+ if (tit )
390+ dfs_cache_noreq_update_tgthint (refpath , tit );
391+
392+ dfs_cache_free_tgts (& tl );
393+
394+ /* Need to set up echo worker again once connection has been established */
408395 if (server -> tcpStatus == CifsNeedNegotiate )
409396 mod_delayed_work (cifsiod_wq , & server -> echo , 0 );
410397
411398 wake_up (& server -> response_q );
412399 return rc ;
413400}
414401
402+ int cifs_reconnect (struct TCP_Server_Info * server )
403+ {
404+ int rc ;
405+ struct super_block * sb ;
406+ struct cifs_sb_info * cifs_sb ;
407+
408+ /*
409+ * If tcp session is not an dfs connection or it is a channel, then reconnect to last target
410+ * server.
411+ */
412+ spin_lock (& cifs_tcp_ses_lock );
413+ if (!server -> is_dfs_conn || server -> is_channel ) {
414+ spin_unlock (& cifs_tcp_ses_lock );
415+ return __cifs_reconnect (server );
416+ }
417+ spin_unlock (& cifs_tcp_ses_lock );
418+
419+ /* If no superblock, then it might be an ipc connection */
420+ sb = cifs_get_tcp_super (server );
421+ if (IS_ERR (sb ))
422+ return __cifs_reconnect (server );
423+
424+ /*
425+ * Check for a referral path to look up in superblock. If unset, then simply reconnect to
426+ * last target server.
427+ */
428+ cifs_sb = CIFS_SB (sb );
429+ if (!cifs_sb -> origin_fullpath || !cifs_sb -> origin_fullpath [0 ])
430+ rc = __cifs_reconnect (server );
431+ else
432+ rc = reconnect_dfs_server (server , cifs_sb );
433+
434+ cifs_put_tcp_super (sb );
435+ return rc ;
436+ }
437+ #else
438+ int cifs_reconnect (struct TCP_Server_Info * server )
439+ {
440+ return __cifs_reconnect (server );
441+ }
442+ #endif
443+
415444static void
416445cifs_echo_request (struct work_struct * work )
417446{
0 commit comments