Skip to content

Commit bbcce36

Browse files
Paulo Alcantarasmfrench
authored andcommitted
cifs: split out dfs code from cifs_reconnect()
Make two separate functions that handle dfs and non-dfs reconnect logics since cifs_reconnect() became way too complex to handle both. While at it, add some documentation. Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Reviewed-by: Shyam Prasad N <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent ae0abb4 commit bbcce36

File tree

1 file changed

+162
-133
lines changed

1 file changed

+162
-133
lines changed

fs/cifs/connect.c

Lines changed: 162 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
415444
static void
416445
cifs_echo_request(struct work_struct *work)
417446
{

0 commit comments

Comments
 (0)