Skip to content

Commit 1d04a6f

Browse files
Paulo Alcantarasmfrench
authored andcommitted
cifs: don't block in dfs_cache_noreq_update_tgthint()
Avoid blocking in dfs_cache_noreq_update_tgthint() while reconnecting servers or tcons as the cache refresh worker or new mounts might already be updating their targets. Move some more dfs related code out of connect.c while at it. Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 8332858 commit 1d04a6f

File tree

4 files changed

+269
-274
lines changed

4 files changed

+269
-274
lines changed

fs/cifs/connect.c

Lines changed: 2 additions & 262 deletions
Original file line numberDiff line numberDiff line change
@@ -529,9 +529,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
529529
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
530530
} while (server->tcpStatus == CifsNeedReconnect);
531531

532-
if (target_hint)
533-
dfs_cache_noreq_update_tgthint(refpath, target_hint);
534-
532+
dfs_cache_noreq_update_tgthint(refpath, target_hint);
535533
dfs_cache_free_tgts(&tl);
536534

537535
/* Need to set up echo worker again once connection has been established */
@@ -4079,265 +4077,7 @@ cifs_prune_tlinks(struct work_struct *work)
40794077
TLINK_IDLE_EXPIRE);
40804078
}
40814079

4082-
#ifdef CONFIG_CIFS_DFS_UPCALL
4083-
/* Update dfs referral path of superblock */
4084-
static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
4085-
const char *target)
4086-
{
4087-
int rc = 0;
4088-
size_t len = strlen(target);
4089-
char *refpath, *npath;
4090-
4091-
if (unlikely(len < 2 || *target != '\\'))
4092-
return -EINVAL;
4093-
4094-
if (target[1] == '\\') {
4095-
len += 1;
4096-
refpath = kmalloc(len, GFP_KERNEL);
4097-
if (!refpath)
4098-
return -ENOMEM;
4099-
4100-
scnprintf(refpath, len, "%s", target);
4101-
} else {
4102-
len += sizeof("\\");
4103-
refpath = kmalloc(len, GFP_KERNEL);
4104-
if (!refpath)
4105-
return -ENOMEM;
4106-
4107-
scnprintf(refpath, len, "\\%s", target);
4108-
}
4109-
4110-
npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
4111-
kfree(refpath);
4112-
4113-
if (IS_ERR(npath)) {
4114-
rc = PTR_ERR(npath);
4115-
} else {
4116-
mutex_lock(&server->refpath_lock);
4117-
kfree(server->leaf_fullpath);
4118-
server->leaf_fullpath = npath;
4119-
mutex_unlock(&server->refpath_lock);
4120-
server->current_fullpath = server->leaf_fullpath;
4121-
}
4122-
return rc;
4123-
}
4124-
4125-
static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host,
4126-
size_t tcp_host_len, char *share, bool *target_match)
4127-
{
4128-
int rc = 0;
4129-
const char *dfs_host;
4130-
size_t dfs_host_len;
4131-
4132-
*target_match = true;
4133-
extract_unc_hostname(share, &dfs_host, &dfs_host_len);
4134-
4135-
/* Check if hostnames or addresses match */
4136-
if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
4137-
cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
4138-
dfs_host, (int)tcp_host_len, tcp_host);
4139-
rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
4140-
if (rc)
4141-
cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
4142-
}
4143-
return rc;
4144-
}
4145-
4146-
static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
4147-
struct cifs_sb_info *cifs_sb, char *tree, bool islink,
4148-
struct dfs_cache_tgt_list *tl)
4149-
{
4150-
int rc;
4151-
struct TCP_Server_Info *server = tcon->ses->server;
4152-
const struct smb_version_operations *ops = server->ops;
4153-
struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
4154-
struct cifs_tcon *ipc = root_ses->tcon_ipc;
4155-
char *share = NULL, *prefix = NULL;
4156-
const char *tcp_host;
4157-
size_t tcp_host_len;
4158-
struct dfs_cache_tgt_iterator *tit;
4159-
bool target_match;
4160-
4161-
extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
4162-
4163-
tit = dfs_cache_get_tgt_iterator(tl);
4164-
if (!tit) {
4165-
rc = -ENOENT;
4166-
goto out;
4167-
}
4168-
4169-
/* Try to tree connect to all dfs targets */
4170-
for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
4171-
const char *target = dfs_cache_get_tgt_name(tit);
4172-
struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl);
4173-
4174-
kfree(share);
4175-
kfree(prefix);
4176-
share = prefix = NULL;
4177-
4178-
/* Check if share matches with tcp ses */
4179-
rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix);
4180-
if (rc) {
4181-
cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
4182-
break;
4183-
}
4184-
4185-
rc = target_share_matches_server(server, tcp_host, tcp_host_len, share,
4186-
&target_match);
4187-
if (rc)
4188-
break;
4189-
if (!target_match) {
4190-
rc = -EHOSTUNREACH;
4191-
continue;
4192-
}
4193-
4194-
if (ipc->need_reconnect) {
4195-
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
4196-
rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls);
4197-
if (rc)
4198-
break;
4199-
}
4200-
4201-
scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
4202-
if (!islink) {
4203-
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
4204-
break;
4205-
}
4206-
/*
4207-
* If no dfs referrals were returned from link target, then just do a TREE_CONNECT
4208-
* to it. Otherwise, cache the dfs referral and then mark current tcp ses for
4209-
* reconnect so either the demultiplex thread or the echo worker will reconnect to
4210-
* newly resolved target.
4211-
*/
4212-
if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
4213-
NULL, &ntl)) {
4214-
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
4215-
if (rc)
4216-
continue;
4217-
rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit);
4218-
if (!rc)
4219-
rc = cifs_update_super_prepath(cifs_sb, prefix);
4220-
} else {
4221-
/* Target is another dfs share */
4222-
rc = update_server_fullpath(server, cifs_sb, target);
4223-
dfs_cache_free_tgts(tl);
4224-
4225-
if (!rc) {
4226-
rc = -EREMOTE;
4227-
list_replace_init(&ntl.tl_list, &tl->tl_list);
4228-
} else
4229-
dfs_cache_free_tgts(&ntl);
4230-
}
4231-
break;
4232-
}
4233-
4234-
out:
4235-
kfree(share);
4236-
kfree(prefix);
4237-
4238-
return rc;
4239-
}
4240-
4241-
static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
4242-
struct cifs_sb_info *cifs_sb, char *tree, bool islink,
4243-
struct dfs_cache_tgt_list *tl)
4244-
{
4245-
int rc;
4246-
int num_links = 0;
4247-
struct TCP_Server_Info *server = tcon->ses->server;
4248-
4249-
do {
4250-
rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl);
4251-
if (!rc || rc != -EREMOTE)
4252-
break;
4253-
} while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
4254-
/*
4255-
* If we couldn't tree connect to any targets from last referral path, then retry from
4256-
* original referral path.
4257-
*/
4258-
if (rc && server->current_fullpath != server->origin_fullpath) {
4259-
server->current_fullpath = server->origin_fullpath;
4260-
cifs_signal_cifsd_for_reconnect(server, true);
4261-
}
4262-
4263-
dfs_cache_free_tgts(tl);
4264-
return rc;
4265-
}
4266-
4267-
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
4268-
{
4269-
int rc;
4270-
struct TCP_Server_Info *server = tcon->ses->server;
4271-
const struct smb_version_operations *ops = server->ops;
4272-
struct super_block *sb = NULL;
4273-
struct cifs_sb_info *cifs_sb;
4274-
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
4275-
char *tree;
4276-
struct dfs_info3_param ref = {0};
4277-
4278-
/* only send once per connect */
4279-
spin_lock(&tcon->tc_lock);
4280-
if (tcon->ses->ses_status != SES_GOOD ||
4281-
(tcon->status != TID_NEW &&
4282-
tcon->status != TID_NEED_TCON)) {
4283-
spin_unlock(&tcon->tc_lock);
4284-
return 0;
4285-
}
4286-
tcon->status = TID_IN_TCON;
4287-
spin_unlock(&tcon->tc_lock);
4288-
4289-
tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
4290-
if (!tree) {
4291-
rc = -ENOMEM;
4292-
goto out;
4293-
}
4294-
4295-
if (tcon->ipc) {
4296-
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
4297-
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
4298-
goto out;
4299-
}
4300-
4301-
sb = cifs_get_tcp_super(server);
4302-
if (IS_ERR(sb)) {
4303-
rc = PTR_ERR(sb);
4304-
cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc);
4305-
goto out;
4306-
}
4307-
4308-
cifs_sb = CIFS_SB(sb);
4309-
4310-
/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
4311-
if (!server->current_fullpath ||
4312-
dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) {
4313-
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls);
4314-
goto out;
4315-
}
4316-
4317-
rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK,
4318-
&tl);
4319-
free_dfs_info_param(&ref);
4320-
4321-
out:
4322-
kfree(tree);
4323-
cifs_put_tcp_super(sb);
4324-
4325-
if (rc) {
4326-
spin_lock(&tcon->tc_lock);
4327-
if (tcon->status == TID_IN_TCON)
4328-
tcon->status = TID_NEED_TCON;
4329-
spin_unlock(&tcon->tc_lock);
4330-
} else {
4331-
spin_lock(&tcon->tc_lock);
4332-
if (tcon->status == TID_IN_TCON)
4333-
tcon->status = TID_GOOD;
4334-
spin_unlock(&tcon->tc_lock);
4335-
tcon->need_reconnect = false;
4336-
}
4337-
4338-
return rc;
4339-
}
4340-
#else
4080+
#ifndef CONFIG_CIFS_DFS_UPCALL
43414081
int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
43424082
{
43434083
int rc;

0 commit comments

Comments
 (0)