Commit c9f71103 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French

cifs: keep referral server sessions alive

At every mount, keep all sessions alive that were used for chasing the
DFS referrals as long as the dfs mounts are active.

Use those sessions in DFS cache to refresh all active tcons as well as
cached entries.  They will be managed by a list of mount_group
structures that will be indexed by a randomly generated uuid at mount
time, so we can put all the sessions related to specific dfs mounts
and avoid leaking them.
Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@cjr.nz>
Reviewed-by: default avatarAurelien Aptel <aaptel@suse.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 2b133b7e
......@@ -77,6 +77,8 @@ struct cifs_sb_info {
* failover properly.
*/
char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */
/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
uuid_t dfs_mount_id;
/*
* Indicate whether serverino option was turned off later
* (cifs_autodisable_serverino) in order to match new mounts.
......
......@@ -368,13 +368,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n",
__func__, rc);
}
rc = dfs_cache_update_vol(cifs_sb->origin_fullpath, server);
if (rc) {
cifs_server_dbg(VFS, "%s: failed to update vol info in DFS cache: rc = %d\n",
__func__, rc);
}
dfs_cache_free_tgts(&tgt_list);
}
cifs_put_tcp_super(sb);
......@@ -1601,7 +1595,6 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
{
unsigned int rc, xid;
struct TCP_Server_Info *server = ses->server;
cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
spin_lock(&cifs_tcp_ses_lock);
......@@ -1609,6 +1602,10 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
spin_unlock(&cifs_tcp_ses_lock);
return;
}
cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
cifs_dbg(FYI, "%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->treeName : "NONE");
if (--ses->ses_count > 0) {
spin_unlock(&cifs_tcp_ses_lock);
return;
......@@ -3284,25 +3281,23 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *
}
#ifdef CONFIG_CIFS_DFS_UPCALL
static void set_root_ses(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
static void set_root_ses(struct cifs_sb_info *cifs_sb, const uuid_t *mount_id, struct cifs_ses *ses,
struct cifs_ses **root_ses)
{
if (ses) {
spin_lock(&cifs_tcp_ses_lock);
ses->ses_count++;
if (ses->tcon_ipc)
cifs_dbg(FYI, "%s: new ses_count=%d\n", __func__, ses->ses_count);
if (ses->tcon_ipc) {
cifs_dbg(FYI, "%s: ipc tcon: %s\n", __func__, ses->tcon_ipc->treeName);
ses->tcon_ipc->remap = cifs_remap(cifs_sb);
}
spin_unlock(&cifs_tcp_ses_lock);
dfs_cache_add_refsrv_session(mount_id, ses);
}
*root_ses = ses;
}
static void put_root_ses(struct cifs_ses *ses)
{
if (ses)
cifs_put_smb_ses(ses);
}
/* Set up next dfs prefix path in @dfs_path */
static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
const unsigned int xid, struct TCP_Server_Info *server,
......@@ -3382,9 +3377,9 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
struct cifs_ses *ses = NULL, *root_ses = NULL;
struct cifs_tcon *tcon = NULL;
int count = 0;
uuid_t mount_id = {0};
char *ref_path = NULL, *full_path = NULL;
char *oldmnt = NULL;
char *mntdata = NULL;
bool ref_server = false;
rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
......@@ -3407,12 +3402,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
if (rc != -EREMOTE)
goto error;
}
/* Save mount options */
mntdata = kstrdup(cifs_sb->ctx->mount_options, GFP_KERNEL);
if (!mntdata) {
rc = -ENOMEM;
goto error;
}
/* Get path of DFS root */
ref_path = build_unc_path_to_root(ctx, cifs_sb, false);
if (IS_ERR(ref_path)) {
......@@ -3421,7 +3411,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
goto error;
}
set_root_ses(cifs_sb, ses, &root_ses);
uuid_gen(&mount_id);
set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
do {
/* Save full path of last DFS path we used to resolve final target server */
kfree(full_path);
......@@ -3455,10 +3446,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
rc = is_referral_server(ref_path + 1, tcon, &ref_server);
if (rc)
break;
if (ref_server) {
put_root_ses(root_ses);
set_root_ses(cifs_sb, ses, &root_ses);
}
if (ref_server)
set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
/* Get next dfs path and then continue chasing them if -EREMOTE */
rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
......@@ -3469,8 +3458,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
if (rc)
goto error;
put_root_ses(root_ses);
root_ses = NULL;
kfree(ref_path);
ref_path = NULL;
/*
......@@ -3492,10 +3480,6 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
tcon->remap = cifs_remap(cifs_sb);
spin_unlock(&cifs_tcp_ses_lock);
/* Add original context for DFS cache to be used when refreshing referrals */
rc = dfs_cache_add_vol(mntdata, ctx, cifs_sb->origin_fullpath);
if (rc)
goto error;
/*
* After reconnecting to a different server, unique ids won't
* match anymore, so we disable serverino. This prevents
......@@ -3510,6 +3494,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
kfree(cifs_sb->prepath);
cifs_sb->prepath = ctx->prepath;
ctx->prepath = NULL;
uuid_copy(&cifs_sb->dfs_mount_id, &mount_id);
out:
free_xid(xid);
......@@ -3519,9 +3504,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
error:
kfree(ref_path);
kfree(full_path);
kfree(mntdata);
kfree(cifs_sb->origin_fullpath);
put_root_ses(root_ses);
dfs_cache_put_refsrv_sessions(&mount_id);
mount_put_conns(cifs_sb, xid, server, ses, tcon);
return rc;
}
......@@ -3751,7 +3735,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
kfree(cifs_sb->prepath);
#ifdef CONFIG_CIFS_DFS_UPCALL
dfs_cache_del_vol(cifs_sb->origin_fullpath);
dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id);
kfree(cifs_sb->origin_fullpath);
#endif
call_rcu(&cifs_sb->rcu, delayed_free);
......
This diff is collapsed.
......@@ -10,6 +10,7 @@
#include <linux/nls.h>
#include <linux/list.h>
#include <linux/uuid.h>
#include "cifsglob.h"
struct dfs_cache_tgt_list {
......@@ -44,13 +45,10 @@ dfs_cache_noreq_update_tgthint(const char *path,
extern int dfs_cache_get_tgt_referral(const char *path,
const struct dfs_cache_tgt_iterator *it,
struct dfs_info3_param *ref);
extern int dfs_cache_add_vol(char *mntdata, struct smb3_fs_context *ctx,
const char *fullpath);
extern int dfs_cache_update_vol(const char *fullpath,
struct TCP_Server_Info *server);
extern void dfs_cache_del_vol(const char *fullpath);
extern int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
char **share, char **prefix);
void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id);
void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses);
static inline struct dfs_cache_tgt_iterator *
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment