mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-12 05:48:39 +08:00
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) <pc@cjr.nz> Reviewed-by: Shyam Prasad N <sprasad@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
ae0abb4dac
commit
bbcce36804
@ -148,57 +148,6 @@ static void cifs_resolve_server(struct work_struct *work)
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
/* These functions must be called with server->srv_mutex held */
|
||||
static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
struct dfs_cache_tgt_list *tgt_list,
|
||||
struct dfs_cache_tgt_iterator **tgt_it)
|
||||
{
|
||||
const char *name;
|
||||
int rc;
|
||||
|
||||
if (!cifs_sb || !cifs_sb->origin_fullpath)
|
||||
return;
|
||||
|
||||
if (!*tgt_it) {
|
||||
*tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
|
||||
} else {
|
||||
*tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it);
|
||||
if (!*tgt_it)
|
||||
*tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "%s: UNC: %s\n", __func__, cifs_sb->origin_fullpath);
|
||||
|
||||
name = dfs_cache_get_tgt_name(*tgt_it);
|
||||
|
||||
kfree(server->hostname);
|
||||
|
||||
server->hostname = extract_hostname(name);
|
||||
if (IS_ERR(server->hostname)) {
|
||||
cifs_dbg(FYI,
|
||||
"%s: failed to extract hostname from target: %ld\n",
|
||||
__func__, PTR_ERR(server->hostname));
|
||||
return;
|
||||
}
|
||||
|
||||
rc = reconn_set_ipaddr_from_hostname(server);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb,
|
||||
struct dfs_cache_tgt_list *tl)
|
||||
{
|
||||
if (!cifs_sb->origin_fullpath)
|
||||
return -EOPNOTSUPP;
|
||||
return dfs_cache_noreq_find(cifs_sb->origin_fullpath + 1, NULL, tl);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Mark all sessions and tcons for reconnect.
|
||||
*
|
||||
@ -278,6 +227,21 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
|
||||
}
|
||||
}
|
||||
|
||||
static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
|
||||
{
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
server->nr_targets = num_targets;
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
/* the demux thread will exit normally next time through the loop */
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
wake_up(&server->response_q);
|
||||
return false;
|
||||
}
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* cifs tcp session reconnection
|
||||
*
|
||||
@ -286,90 +250,23 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
|
||||
* reconnect tcp session
|
||||
* wake up waiters on reconnection? - (not needed currently)
|
||||
*/
|
||||
int
|
||||
cifs_reconnect(struct TCP_Server_Info *server)
|
||||
static int __cifs_reconnect(struct TCP_Server_Info *server)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
struct super_block *sb = NULL;
|
||||
struct cifs_sb_info *cifs_sb = NULL;
|
||||
struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
|
||||
struct dfs_cache_tgt_iterator *tgt_it = NULL;
|
||||
#endif
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
server->nr_targets = 1;
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
sb = cifs_get_tcp_super(server);
|
||||
if (IS_ERR(sb)) {
|
||||
rc = PTR_ERR(sb);
|
||||
cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
|
||||
__func__, rc);
|
||||
sb = NULL;
|
||||
} else {
|
||||
cifs_sb = CIFS_SB(sb);
|
||||
rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list);
|
||||
if (rc) {
|
||||
cifs_sb = NULL;
|
||||
if (rc != -EOPNOTSUPP) {
|
||||
cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
|
||||
__func__);
|
||||
}
|
||||
} else {
|
||||
server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);
|
||||
}
|
||||
}
|
||||
cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
|
||||
server->nr_targets);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
#endif
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
/* the demux thread will exit normally next time through the loop */
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
dfs_cache_free_tgts(&tgt_list);
|
||||
cifs_put_tcp_super(sb);
|
||||
#endif
|
||||
wake_up(&server->response_q);
|
||||
return rc;
|
||||
} else
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
if (!cifs_tcp_ses_needs_reconnect(server, 1))
|
||||
return 0;
|
||||
|
||||
cifs_mark_tcp_ses_conns_for_reconnect(server);
|
||||
|
||||
do {
|
||||
try_to_freeze();
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
|
||||
|
||||
if (!cifs_swn_set_server_dstaddr(server)) {
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
if (cifs_sb && cifs_sb->origin_fullpath)
|
||||
/*
|
||||
* Set up next DFS target server (if any) for reconnect. If DFS
|
||||
* feature is disabled, then we will retry last server we
|
||||
* connected to before.
|
||||
*/
|
||||
reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
|
||||
else {
|
||||
#endif
|
||||
/*
|
||||
* Resolve the hostname again to make sure that IP address is up-to-date.
|
||||
*/
|
||||
/* resolve the hostname again to make sure that IP address is up-to-date */
|
||||
rc = reconn_set_ipaddr_from_hostname(server);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
|
||||
}
|
||||
|
||||
if (cifs_rdma_enabled(server))
|
||||
@ -377,8 +274,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
else
|
||||
rc = generic_ip_connect(server);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "reconnect error %d\n", rc);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
|
||||
msleep(3000);
|
||||
} else {
|
||||
atomic_inc(&tcpSesReconnectCount);
|
||||
@ -392,19 +289,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
}
|
||||
} while (server->tcpStatus == CifsNeedReconnect);
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
if (tgt_it) {
|
||||
rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1,
|
||||
tgt_it);
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
dfs_cache_free_tgts(&tgt_list);
|
||||
}
|
||||
|
||||
cifs_put_tcp_super(sb);
|
||||
#endif
|
||||
if (server->tcpStatus == CifsNeedNegotiate)
|
||||
mod_delayed_work(cifsiod_wq, &server->echo, 0);
|
||||
|
||||
@ -412,6 +296,151 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *refpath = cifs_sb->origin_fullpath + 1;
|
||||
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
|
||||
struct dfs_cache_tgt_iterator *tit = NULL;
|
||||
int num_targets = 1;
|
||||
char *hostname;
|
||||
|
||||
/*
|
||||
* Determine the number of dfs targets the referral path in @cifs_sb resolves to.
|
||||
*
|
||||
* smb2_reconnect() needs to know how long it should wait based upon the number of dfs
|
||||
* targets (server->nr_targets). It's also possible that the cached referral was cleared
|
||||
* through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
|
||||
* refreshing the referral, so, in this case, default it to 1.
|
||||
*/
|
||||
if (!dfs_cache_noreq_find(refpath, NULL, &tl)) {
|
||||
num_targets = dfs_cache_get_nr_tgts(&tl);
|
||||
if (!num_targets)
|
||||
num_targets = 1;
|
||||
}
|
||||
|
||||
if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
|
||||
return 0;
|
||||
|
||||
cifs_mark_tcp_ses_conns_for_reconnect(server);
|
||||
|
||||
do {
|
||||
/* Get next dfs target from target list (if any) */
|
||||
if (!tit)
|
||||
tit = dfs_cache_get_tgt_iterator(&tl);
|
||||
else
|
||||
tit = dfs_cache_get_next_tgt(&tl, tit);
|
||||
|
||||
try_to_freeze();
|
||||
mutex_lock(&server->srv_mutex);
|
||||
|
||||
if (!cifs_swn_set_server_dstaddr(server)) {
|
||||
/*
|
||||
* If any dfs target was selected, then update @server with either a
|
||||
* hostname or an address.
|
||||
*/
|
||||
if (tit) {
|
||||
hostname = extract_hostname(dfs_cache_get_tgt_name(tit));
|
||||
if (!IS_ERR(hostname)) {
|
||||
kfree(server->hostname);
|
||||
server->hostname = hostname;
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
|
||||
__func__, PTR_ERR(hostname));
|
||||
cifs_dbg(FYI, "%s: default to last target server: %s\n",
|
||||
__func__, server->hostname);
|
||||
}
|
||||
}
|
||||
/* resolve the hostname again to make sure that IP address is up-to-date. */
|
||||
rc = reconn_set_ipaddr_from_hostname(server);
|
||||
cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
|
||||
}
|
||||
|
||||
/* Reconnect the socket */
|
||||
if (cifs_rdma_enabled(server))
|
||||
rc = smbd_reconnect(server);
|
||||
else
|
||||
rc = generic_ip_connect(server);
|
||||
|
||||
if (rc) {
|
||||
/* Failed to reconnect socket. Retry next dfs target. */
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
|
||||
msleep(3000);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Socket was created. Update tcp session status to CifsNeedNegotiate so that a
|
||||
* process waiting for reconnect will know it needs to re-establish session and tcon
|
||||
* through the reconnected target server.
|
||||
*/
|
||||
atomic_inc(&tcpSesReconnectCount);
|
||||
set_credits(server, 1);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (server->tcpStatus != CifsExiting)
|
||||
server->tcpStatus = CifsNeedNegotiate;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
cifs_swn_reset_server_dstaddr(server);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
} while (server->tcpStatus == CifsNeedReconnect);
|
||||
|
||||
if (tit)
|
||||
dfs_cache_noreq_update_tgthint(refpath, tit);
|
||||
|
||||
dfs_cache_free_tgts(&tl);
|
||||
|
||||
/* Need to set up echo worker again once connection has been established */
|
||||
if (server->tcpStatus == CifsNeedNegotiate)
|
||||
mod_delayed_work(cifsiod_wq, &server->echo, 0);
|
||||
|
||||
wake_up(&server->response_q);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cifs_reconnect(struct TCP_Server_Info *server)
|
||||
{
|
||||
int rc;
|
||||
struct super_block *sb;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
|
||||
/*
|
||||
* If tcp session is not an dfs connection or it is a channel, then reconnect to last target
|
||||
* server.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (!server->is_dfs_conn || server->is_channel) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return __cifs_reconnect(server);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* If no superblock, then it might be an ipc connection */
|
||||
sb = cifs_get_tcp_super(server);
|
||||
if (IS_ERR(sb))
|
||||
return __cifs_reconnect(server);
|
||||
|
||||
/*
|
||||
* Check for a referral path to look up in superblock. If unset, then simply reconnect to
|
||||
* last target server.
|
||||
*/
|
||||
cifs_sb = CIFS_SB(sb);
|
||||
if (!cifs_sb->origin_fullpath || !cifs_sb->origin_fullpath[0])
|
||||
rc = __cifs_reconnect(server);
|
||||
else
|
||||
rc = reconnect_dfs_server(server, cifs_sb);
|
||||
|
||||
cifs_put_tcp_super(sb);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
int cifs_reconnect(struct TCP_Server_Info *server)
|
||||
{
|
||||
return __cifs_reconnect(server);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
cifs_echo_request(struct work_struct *work)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user