Skip to content

Commit 6916881

Browse files
Paulo Alcantarasmfrench
authored andcommitted
cifs: fix refresh of cached referrals
We can't rely on cifs_tcon::ses to refresh cached referral as the server target might not respond to referrals, e.g. share is not hosted in a DFS root server. Consider the following mount //dom/dfs/link -> /root1/dfs/link -> /fs0/share where fs0 can't get a referral for "/root1/dfs/link". To simplify and fix the access of dfs root sessions, store the dfs root session pointer directly to new sessions so making it easier to select the appropriate ipc connection and use it for failover or cache refresh. Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent cb3f6d8 commit 6916881

File tree

4 files changed

+37
-117
lines changed

4 files changed

+37
-117
lines changed

fs/cifs/cifsglob.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@
107107

108108
#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */
109109

110+
#define CIFS_DFS_ROOT_SES(ses) ((ses)->dfs_root_ses ?: (ses))
111+
110112
/*
111113
* CIFS vfs client Status information (based on what we know.)
112114
*/
@@ -1099,6 +1101,7 @@ struct cifs_ses {
10991101
*/
11001102
unsigned long chans_need_reconnect;
11011103
/* ========= end: protected by chan_lock ======== */
1104+
struct cifs_ses *dfs_root_ses;
11021105
};
11031106

11041107
static inline bool

fs/cifs/connect.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4150,7 +4150,8 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
41504150
int rc;
41514151
struct TCP_Server_Info *server = tcon->ses->server;
41524152
const struct smb_version_operations *ops = server->ops;
4153-
struct cifs_tcon *ipc = tcon->ses->tcon_ipc;
4153+
struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
4154+
struct cifs_tcon *ipc = root_ses->tcon_ipc;
41544155
char *share = NULL, *prefix = NULL;
41554156
const char *tcp_host;
41564157
size_t tcp_host_len;
@@ -4208,7 +4209,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
42084209
* reconnect so either the demultiplex thread or the echo worker will reconnect to
42094210
* newly resolved target.
42104211
*/
4211-
if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
4212+
if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
42124213
NULL, &ntl)) {
42134214
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
42144215
if (rc)

fs/cifs/dfs.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,13 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
9595
ctx->leaf_fullpath = (char *)full_path;
9696
rc = cifs_mount_get_session(mnt_ctx);
9797
ctx->leaf_fullpath = NULL;
98+
if (!rc) {
99+
struct cifs_ses *ses = mnt_ctx->ses;
98100

101+
mutex_lock(&ses->session_mutex);
102+
ses->dfs_root_ses = mnt_ctx->root_ses;
103+
mutex_unlock(&ses->session_mutex);
104+
}
99105
return rc;
100106
}
101107

fs/cifs/dfs_cache.c

Lines changed: 25 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -83,27 +83,6 @@ static void refresh_cache_worker(struct work_struct *work);
8383

8484
static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker);
8585

86-
static void get_ipc_unc(const char *ref_path, char *ipc, size_t ipclen)
87-
{
88-
const char *host;
89-
size_t len;
90-
91-
extract_unc_hostname(ref_path, &host, &len);
92-
scnprintf(ipc, ipclen, "\\\\%.*s\\IPC$", (int)len, host);
93-
}
94-
95-
static struct cifs_ses *find_ipc_from_server_path(struct cifs_ses **ses, const char *path)
96-
{
97-
char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
98-
99-
get_ipc_unc(path, unc, sizeof(unc));
100-
for (; *ses; ses++) {
101-
if (!strcasecmp(unc, (*ses)->tcon_ipc->tree_name))
102-
return *ses;
103-
}
104-
return ERR_PTR(-ENOENT);
105-
}
106-
10786
static void __mount_group_release(struct mount_group *mg)
10887
{
10988
int i;
@@ -760,8 +739,6 @@ static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const
760739
int rc;
761740
int i;
762741

763-
cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path);
764-
765742
*refs = NULL;
766743
*numrefs = 0;
767744

@@ -770,6 +747,7 @@ static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const
770747
if (unlikely(!cache_cp))
771748
return -EINVAL;
772749

750+
cifs_dbg(FYI, "%s: ipc=%s referral=%s\n", __func__, ses->tcon_ipc->tree_name, path);
773751
rc = ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp,
774752
NO_MAP_UNI_RSVD);
775753
if (!rc) {
@@ -1366,10 +1344,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach
13661344
}
13671345

13681346
/* Refresh dfs referral of tcon and mark it for reconnect if needed */
1369-
static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon,
1370-
bool force_refresh)
1347+
static int __refresh_tcon(const char *path, struct cifs_tcon *tcon, bool force_refresh)
13711348
{
1372-
struct cifs_ses *ses;
1349+
struct cifs_ses *ses = CIFS_DFS_ROOT_SES(tcon->ses);
13731350
struct cache_entry *ce;
13741351
struct dfs_info3_param *refs = NULL;
13751352
int numrefs = 0;
@@ -1378,11 +1355,7 @@ static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct c
13781355
int rc = 0;
13791356
unsigned int xid;
13801357

1381-
ses = find_ipc_from_server_path(sessions, path);
1382-
if (IS_ERR(ses)) {
1383-
cifs_dbg(FYI, "%s: could not find ipc session\n", __func__);
1384-
return PTR_ERR(ses);
1385-
}
1358+
xid = get_xid();
13861359

13871360
down_read(&htable_rw_lock);
13881361
ce = lookup_cache_entry(path);
@@ -1399,12 +1372,9 @@ static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct c
13991372
goto out;
14001373
}
14011374

1402-
xid = get_xid();
14031375
rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
1404-
free_xid(xid);
1405-
1406-
/* Create or update a cache entry with the new referral */
14071376
if (!rc) {
1377+
/* Create or update a cache entry with the new referral */
14081378
dump_refs(refs, numrefs);
14091379

14101380
down_write(&htable_rw_lock);
@@ -1419,24 +1389,20 @@ static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct c
14191389
}
14201390

14211391
out:
1392+
free_xid(xid);
14221393
dfs_cache_free_tgts(&tl);
14231394
free_dfs_info_array(refs, numrefs);
14241395
return rc;
14251396
}
14261397

1427-
static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh)
1398+
static int refresh_tcon(struct cifs_tcon *tcon, bool force_refresh)
14281399
{
14291400
struct TCP_Server_Info *server = tcon->ses->server;
14301401

14311402
mutex_lock(&server->refpath_lock);
1432-
if (server->origin_fullpath) {
1433-
if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath,
1434-
server->origin_fullpath))
1435-
__refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh);
1436-
__refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh);
1437-
}
1403+
if (server->leaf_fullpath)
1404+
__refresh_tcon(server->leaf_fullpath + 1, tcon, force_refresh);
14381405
mutex_unlock(&server->refpath_lock);
1439-
14401406
return 0;
14411407
}
14421408

@@ -1454,9 +1420,6 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
14541420
{
14551421
struct cifs_tcon *tcon;
14561422
struct TCP_Server_Info *server;
1457-
struct mount_group *mg;
1458-
struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL};
1459-
int rc;
14601423

14611424
if (!cifs_sb || !cifs_sb->master_tlink)
14621425
return -EINVAL;
@@ -1473,21 +1436,6 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
14731436
cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__);
14741437
return -EINVAL;
14751438
}
1476-
1477-
mutex_lock(&mount_group_list_lock);
1478-
mg = find_mount_group_locked(&cifs_sb->dfs_mount_id);
1479-
if (IS_ERR(mg)) {
1480-
mutex_unlock(&mount_group_list_lock);
1481-
cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__);
1482-
return PTR_ERR(mg);
1483-
}
1484-
kref_get(&mg->refcount);
1485-
mutex_unlock(&mount_group_list_lock);
1486-
1487-
spin_lock(&mg->lock);
1488-
memcpy(&sessions, mg->sessions, mg->num_sessions * sizeof(mg->sessions[0]));
1489-
spin_unlock(&mg->lock);
1490-
14911439
/*
14921440
* After reconnecting to a different server, unique ids won't match anymore, so we disable
14931441
* 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)
14981446
* that have different prefix paths.
14991447
*/
15001448
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
1501-
rc = refresh_tcon(sessions, tcon, true);
15021449

1503-
kref_put(&mg->refcount, mount_group_release);
1504-
return rc;
1450+
return refresh_tcon(tcon, true);
15051451
}
15061452

15071453
/*
1508-
* Refresh all active dfs mounts regardless of whether they are in cache or not.
1509-
* (cache can be cleared)
1454+
* Worker that will refresh DFS cache from all active mounts based on lowest TTL value
1455+
* from a DFS referral.
15101456
*/
1511-
static void refresh_mounts(struct cifs_ses **sessions)
1457+
static void refresh_cache_worker(struct work_struct *work)
15121458
{
15131459
struct TCP_Server_Info *server;
15141460
struct cifs_ses *ses;
@@ -1523,9 +1469,19 @@ static void refresh_mounts(struct cifs_ses **sessions)
15231469
continue;
15241470

15251471
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
1472+
struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(ses);
1473+
1474+
spin_lock(&root_ses->ses_lock);
1475+
if (root_ses->ses_status != SES_GOOD) {
1476+
spin_unlock(&root_ses->ses_lock);
1477+
continue;
1478+
}
1479+
spin_unlock(&root_ses->ses_lock);
1480+
15261481
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
15271482
spin_lock(&tcon->tc_lock);
1528-
if (!tcon->ipc && !tcon->need_reconnect) {
1483+
if (!tcon->ipc && tcon->status != TID_NEW &&
1484+
tcon->status != TID_NEED_TCON) {
15291485
tcon->tc_count++;
15301486
list_add_tail(&tcon->ulist, &tcons);
15311487
}
@@ -1542,57 +1498,11 @@ static void refresh_mounts(struct cifs_ses **sessions)
15421498

15431499
mutex_lock(&server->refpath_lock);
15441500
if (server->leaf_fullpath)
1545-
__refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false);
1501+
__refresh_tcon(server->leaf_fullpath + 1, tcon, false);
15461502
mutex_unlock(&server->refpath_lock);
15471503

15481504
cifs_put_tcon(tcon);
15491505
}
1550-
}
1551-
1552-
/*
1553-
* Worker that will refresh DFS cache and active mounts based on lowest TTL value from a DFS
1554-
* referral.
1555-
*/
1556-
static void refresh_cache_worker(struct work_struct *work)
1557-
{
1558-
struct list_head mglist;
1559-
struct mount_group *mg, *tmp_mg;
1560-
struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL};
1561-
int max_sessions = ARRAY_SIZE(sessions) - 1;
1562-
int i = 0, count;
1563-
1564-
INIT_LIST_HEAD(&mglist);
1565-
1566-
/* Get refereces of mount groups */
1567-
mutex_lock(&mount_group_list_lock);
1568-
list_for_each_entry(mg, &mount_group_list, list) {
1569-
kref_get(&mg->refcount);
1570-
list_add(&mg->refresh_list, &mglist);
1571-
}
1572-
mutex_unlock(&mount_group_list_lock);
1573-
1574-
/* Fill in local array with an NULL-terminated list of all referral server sessions */
1575-
list_for_each_entry(mg, &mglist, refresh_list) {
1576-
if (i >= max_sessions)
1577-
break;
1578-
1579-
spin_lock(&mg->lock);
1580-
if (i + mg->num_sessions > max_sessions)
1581-
count = max_sessions - i;
1582-
else
1583-
count = mg->num_sessions;
1584-
memcpy(&sessions[i], mg->sessions, count * sizeof(mg->sessions[0]));
1585-
spin_unlock(&mg->lock);
1586-
i += count;
1587-
}
1588-
1589-
if (sessions[0])
1590-
refresh_mounts(sessions);
1591-
1592-
list_for_each_entry_safe(mg, tmp_mg, &mglist, refresh_list) {
1593-
list_del_init(&mg->refresh_list);
1594-
kref_put(&mg->refcount, mount_group_release);
1595-
}
15961506

15971507
spin_lock(&cache_ttl_lock);
15981508
queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);

0 commit comments

Comments
 (0)