diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifsglob.h | 3 | ||||
-rw-r--r-- | fs/cifs/connect.c | 5 | ||||
-rw-r--r-- | fs/cifs/dfs.c | 6 | ||||
-rw-r--r-- | fs/cifs/dfs_cache.c | 140 |
4 files changed, 37 insertions, 117 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2e2976f1874f..cfdd5bf701a1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -107,6 +107,8 @@ #define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */ +#define CIFS_DFS_ROOT_SES(ses) ((ses)->dfs_root_ses ?: (ses)) + /* * CIFS vfs client Status information (based on what we know.) */ @@ -1099,6 +1101,7 @@ struct cifs_ses { */ unsigned long chans_need_reconnect; /* ========= end: protected by chan_lock ======== */ + struct cifs_ses *dfs_root_ses; }; static inline bool diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a66cb23a954e..db3a2b3ac497 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -4150,7 +4150,8 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t int rc; struct TCP_Server_Info *server = tcon->ses->server; const struct smb_version_operations *ops = server->ops; - struct cifs_tcon *ipc = tcon->ses->tcon_ipc; + struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses); + struct cifs_tcon *ipc = root_ses->tcon_ipc; char *share = NULL, *prefix = NULL; const char *tcp_host; size_t tcp_host_len; @@ -4208,7 +4209,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t * reconnect so either the demultiplex thread or the echo worker will reconnect to * newly resolved target. */ - if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, + if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, NULL, &ntl)) { rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); if (rc) diff --git a/fs/cifs/dfs.c b/fs/cifs/dfs.c index 88a0cab335b4..fbc8e880a1fe 100644 --- a/fs/cifs/dfs.c +++ b/fs/cifs/dfs.c @@ -95,7 +95,13 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) ctx->leaf_fullpath = (char *)full_path; rc = cifs_mount_get_session(mnt_ctx); ctx->leaf_fullpath = NULL; + if (!rc) { + struct cifs_ses *ses = mnt_ctx->ses; + mutex_lock(&ses->session_mutex); + ses->dfs_root_ses = mnt_ctx->root_ses; + mutex_unlock(&ses->session_mutex); + } return rc; } diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c index bef672534397..95cc3951420e 100644 --- a/fs/cifs/dfs_cache.c +++ b/fs/cifs/dfs_cache.c @@ -83,27 +83,6 @@ static void refresh_cache_worker(struct work_struct *work); static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker); -static void get_ipc_unc(const char *ref_path, char *ipc, size_t ipclen) -{ - const char *host; - size_t len; - - extract_unc_hostname(ref_path, &host, &len); - scnprintf(ipc, ipclen, "\\\\%.*s\\IPC$", (int)len, host); -} - -static struct cifs_ses *find_ipc_from_server_path(struct cifs_ses **ses, const char *path) -{ - char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; - - get_ipc_unc(path, unc, sizeof(unc)); - for (; *ses; ses++) { - if (!strcasecmp(unc, (*ses)->tcon_ipc->tree_name)) - return *ses; - } - return ERR_PTR(-ENOENT); -} - static void __mount_group_release(struct mount_group *mg) { int i; @@ -760,8 +739,6 @@ static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const int rc; int i; - cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path); - *refs = NULL; *numrefs = 0; @@ -770,6 +747,7 @@ static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const if (unlikely(!cache_cp)) return -EINVAL; + cifs_dbg(FYI, "%s: ipc=%s referral=%s\n", __func__, ses->tcon_ipc->tree_name, path); rc = ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp, NO_MAP_UNI_RSVD); if (!rc) { @@ -1366,10 +1344,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach } /* Refresh dfs referral of tcon and mark it for reconnect if needed */ -static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon, - bool force_refresh) +static int __refresh_tcon(const char *path, struct cifs_tcon *tcon, bool force_refresh) { - struct cifs_ses *ses; + struct cifs_ses *ses = CIFS_DFS_ROOT_SES(tcon->ses); struct cache_entry *ce; struct dfs_info3_param *refs = NULL; int numrefs = 0; @@ -1378,11 +1355,7 @@ static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct c int rc = 0; unsigned int xid; - ses = find_ipc_from_server_path(sessions, path); - if (IS_ERR(ses)) { - cifs_dbg(FYI, "%s: could not find ipc session\n", __func__); - return PTR_ERR(ses); - } + xid = get_xid(); down_read(&htable_rw_lock); ce = lookup_cache_entry(path); @@ -1399,12 +1372,9 @@ static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct c goto out; } - xid = get_xid(); rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); - free_xid(xid); - - /* Create or update a cache entry with the new referral */ if (!rc) { + /* Create or update a cache entry with the new referral */ dump_refs(refs, numrefs); down_write(&htable_rw_lock); @@ -1419,24 +1389,20 @@ static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct c } out: + free_xid(xid); dfs_cache_free_tgts(&tl); free_dfs_info_array(refs, numrefs); return rc; } -static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) +static int refresh_tcon(struct cifs_tcon *tcon, bool force_refresh) { struct TCP_Server_Info *server = tcon->ses->server; mutex_lock(&server->refpath_lock); - if (server->origin_fullpath) { - if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, - server->origin_fullpath)) - __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh); - __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh); - } + if (server->leaf_fullpath) + __refresh_tcon(server->leaf_fullpath + 1, tcon, force_refresh); mutex_unlock(&server->refpath_lock); - return 0; } @@ -1454,9 +1420,6 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) { struct cifs_tcon *tcon; struct TCP_Server_Info *server; - struct mount_group *mg; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; - int rc; if (!cifs_sb || !cifs_sb->master_tlink) return -EINVAL; @@ -1473,21 +1436,6 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__); return -EINVAL; } - - mutex_lock(&mount_group_list_lock); - mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); - if (IS_ERR(mg)) { - mutex_unlock(&mount_group_list_lock); - cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__); - return PTR_ERR(mg); - } - kref_get(&mg->refcount); - mutex_unlock(&mount_group_list_lock); - - spin_lock(&mg->lock); - memcpy(&sessions, mg->sessions, mg->num_sessions * sizeof(mg->sessions[0])); - spin_unlock(&mg->lock); - /* * After reconnecting to a different server, unique ids won't match anymore, so we disable * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). @@ -1498,17 +1446,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) * that have different prefix paths. */ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - rc = refresh_tcon(sessions, tcon, true); - kref_put(&mg->refcount, mount_group_release); - return rc; + return refresh_tcon(tcon, true); } /* - * Refresh all active dfs mounts regardless of whether they are in cache or not. - * (cache can be cleared) + * Worker that will refresh DFS cache from all active mounts based on lowest TTL value + * from a DFS referral. */ -static void refresh_mounts(struct cifs_ses **sessions) +static void refresh_cache_worker(struct work_struct *work) { struct TCP_Server_Info *server; struct cifs_ses *ses; @@ -1523,9 +1469,19 @@ static void refresh_mounts(struct cifs_ses **sessions) continue; list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(ses); + + spin_lock(&root_ses->ses_lock); + if (root_ses->ses_status != SES_GOOD) { + spin_unlock(&root_ses->ses_lock); + continue; + } + spin_unlock(&root_ses->ses_lock); + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { spin_lock(&tcon->tc_lock); - if (!tcon->ipc && !tcon->need_reconnect) { + if (!tcon->ipc && tcon->status != TID_NEW && + tcon->status != TID_NEED_TCON) { tcon->tc_count++; list_add_tail(&tcon->ulist, &tcons); } @@ -1542,57 +1498,11 @@ static void refresh_mounts(struct cifs_ses **sessions) mutex_lock(&server->refpath_lock); if (server->leaf_fullpath) - __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); + __refresh_tcon(server->leaf_fullpath + 1, tcon, false); mutex_unlock(&server->refpath_lock); cifs_put_tcon(tcon); } -} - -/* - * Worker that will refresh DFS cache and active mounts based on lowest TTL value from a DFS - * referral. - */ -static void refresh_cache_worker(struct work_struct *work) -{ - struct list_head mglist; - struct mount_group *mg, *tmp_mg; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; - int max_sessions = ARRAY_SIZE(sessions) - 1; - int i = 0, count; - - INIT_LIST_HEAD(&mglist); - - /* Get refereces of mount groups */ - mutex_lock(&mount_group_list_lock); - list_for_each_entry(mg, &mount_group_list, list) { - kref_get(&mg->refcount); - list_add(&mg->refresh_list, &mglist); - } - mutex_unlock(&mount_group_list_lock); - - /* Fill in local array with an NULL-terminated list of all referral server sessions */ - list_for_each_entry(mg, &mglist, refresh_list) { - if (i >= max_sessions) - break; - - spin_lock(&mg->lock); - if (i + mg->num_sessions > max_sessions) - count = max_sessions - i; - else - count = mg->num_sessions; - memcpy(&sessions[i], mg->sessions, count * sizeof(mg->sessions[0])); - spin_unlock(&mg->lock); - i += count; - } - - if (sessions[0]) - refresh_mounts(sessions); - - list_for_each_entry_safe(mg, tmp_mg, &mglist, refresh_list) { - list_del_init(&mg->refresh_list); - kref_put(&mg->refcount, mount_group_release); - } spin_lock(&cache_ttl_lock); queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); |