@@ -78,6 +78,8 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
78
78
int rc ;
79
79
int len ;
80
80
char * unc , * ipaddr = NULL ;
81
+ time64_t expiry , now ;
82
+ unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT ;
81
83
82
84
if (!server -> hostname )
83
85
return - EINVAL ;
@@ -91,13 +93,13 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
91
93
}
92
94
scnprintf (unc , len , "\\\\%s" , server -> hostname );
93
95
94
- rc = dns_resolve_server_name_to_ip (unc , & ipaddr );
96
+ rc = dns_resolve_server_name_to_ip (unc , & ipaddr , & expiry );
95
97
kfree (unc );
96
98
97
99
if (rc < 0 ) {
98
100
cifs_dbg (FYI , "%s: failed to resolve server part of %s to IP: %d\n" ,
99
101
__func__ , server -> hostname , rc );
100
- return rc ;
102
+ goto requeue_resolve ;
101
103
}
102
104
103
105
spin_lock (& cifs_tcp_ses_lock );
@@ -106,7 +108,45 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
106
108
spin_unlock (& cifs_tcp_ses_lock );
107
109
kfree (ipaddr );
108
110
109
- return !rc ? -1 : 0 ;
111
+ /* rc == 1 means success here */
112
+ if (rc ) {
113
+ now = ktime_get_real_seconds ();
114
+ if (expiry && expiry > now )
115
+ /*
116
+ * To make sure we don't use the cached entry, retry 1s
117
+ * after expiry.
118
+ */
119
+ ttl = (expiry - now + 1 );
120
+ }
121
+ rc = !rc ? -1 : 0 ;
122
+
123
+ requeue_resolve :
124
+ cifs_dbg (FYI , "%s: next dns resolution scheduled for %lu seconds in the future\n" ,
125
+ __func__ , ttl );
126
+ mod_delayed_work (cifsiod_wq , & server -> resolve , (ttl * HZ ));
127
+
128
+ return rc ;
129
+ }
130
+
131
+
132
+ static void cifs_resolve_server (struct work_struct * work )
133
+ {
134
+ int rc ;
135
+ struct TCP_Server_Info * server = container_of (work ,
136
+ struct TCP_Server_Info , resolve .work );
137
+
138
+ mutex_lock (& server -> srv_mutex );
139
+
140
+ /*
141
+ * Resolve the hostname again to make sure that IP address is up-to-date.
142
+ */
143
+ rc = reconn_set_ipaddr_from_hostname (server );
144
+ if (rc ) {
145
+ cifs_dbg (FYI , "%s: failed to resolve hostname: %d\n" ,
146
+ __func__ , rc );
147
+ }
148
+
149
+ mutex_unlock (& server -> srv_mutex );
110
150
}
111
151
112
152
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -680,6 +720,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
680
720
spin_unlock (& cifs_tcp_ses_lock );
681
721
682
722
cancel_delayed_work_sync (& server -> echo );
723
+ cancel_delayed_work_sync (& server -> resolve );
683
724
684
725
spin_lock (& GlobalMid_Lock );
685
726
server -> tcpStatus = CifsExiting ;
@@ -1227,6 +1268,16 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx)
1227
1268
1228
1269
spin_lock (& cifs_tcp_ses_lock );
1229
1270
list_for_each_entry (server , & cifs_tcp_ses_list , tcp_ses_list ) {
1271
+ #ifdef CONFIG_CIFS_DFS_UPCALL
1272
+ /*
1273
+ * DFS failover implementation in cifs_reconnect() requires unique tcp sessions for
1274
+ * DFS connections to do failover properly, so avoid sharing them with regular
1275
+ * shares or even links that may connect to same server but having completely
1276
+ * different failover targets.
1277
+ */
1278
+ if (server -> is_dfs_conn )
1279
+ continue ;
1280
+ #endif
1230
1281
/*
1231
1282
* Skip ses channels since they're only handled in lower layers
1232
1283
* (e.g. cifs_send_recv).
@@ -1254,12 +1305,16 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
1254
1305
return ;
1255
1306
}
1256
1307
1308
+ /* srv_count can never go negative */
1309
+ WARN_ON (server -> srv_count < 0 );
1310
+
1257
1311
put_net (cifs_net_ns (server ));
1258
1312
1259
1313
list_del_init (& server -> tcp_ses_list );
1260
1314
spin_unlock (& cifs_tcp_ses_lock );
1261
1315
1262
1316
cancel_delayed_work_sync (& server -> echo );
1317
+ cancel_delayed_work_sync (& server -> resolve );
1263
1318
1264
1319
if (from_reconnect )
1265
1320
/*
@@ -1342,6 +1397,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
1342
1397
INIT_LIST_HEAD (& tcp_ses -> tcp_ses_list );
1343
1398
INIT_LIST_HEAD (& tcp_ses -> smb_ses_list );
1344
1399
INIT_DELAYED_WORK (& tcp_ses -> echo , cifs_echo_request );
1400
+ INIT_DELAYED_WORK (& tcp_ses -> resolve , cifs_resolve_server );
1345
1401
INIT_DELAYED_WORK (& tcp_ses -> reconnect , smb2_reconnect_server );
1346
1402
mutex_init (& tcp_ses -> reconnect_mutex );
1347
1403
memcpy (& tcp_ses -> srcaddr , & ctx -> srcaddr ,
@@ -1427,6 +1483,12 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
1427
1483
/* queue echo request delayed work */
1428
1484
queue_delayed_work (cifsiod_wq , & tcp_ses -> echo , tcp_ses -> echo_interval );
1429
1485
1486
+ /* queue dns resolution delayed work */
1487
+ cifs_dbg (FYI , "%s: next dns resolution scheduled for %d seconds in the future\n" ,
1488
+ __func__ , SMB_DNS_RESOLVE_INTERVAL_DEFAULT );
1489
+
1490
+ queue_delayed_work (cifsiod_wq , & tcp_ses -> resolve , (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ ));
1491
+
1430
1492
return tcp_ses ;
1431
1493
1432
1494
out_err_crypto_release :
@@ -1605,6 +1667,9 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
1605
1667
}
1606
1668
spin_unlock (& cifs_tcp_ses_lock );
1607
1669
1670
+ /* ses_count can never go negative */
1671
+ WARN_ON (ses -> ses_count < 0 );
1672
+
1608
1673
spin_lock (& GlobalMid_Lock );
1609
1674
if (ses -> status == CifsGood )
1610
1675
ses -> status = CifsExiting ;
@@ -1972,6 +2037,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
1972
2037
return ;
1973
2038
}
1974
2039
2040
+ /* tc_count can never go negative */
2041
+ WARN_ON (tcon -> tc_count < 0 );
2042
+
1975
2043
if (tcon -> use_witness ) {
1976
2044
int rc ;
1977
2045
@@ -2910,6 +2978,23 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
2910
2978
}
2911
2979
2912
2980
#ifdef CONFIG_CIFS_DFS_UPCALL
2981
+ static int mount_get_dfs_conns (struct smb3_fs_context * ctx , struct cifs_sb_info * cifs_sb ,
2982
+ unsigned int * xid , struct TCP_Server_Info * * nserver ,
2983
+ struct cifs_ses * * nses , struct cifs_tcon * * ntcon )
2984
+ {
2985
+ int rc ;
2986
+
2987
+ ctx -> nosharesock = true;
2988
+ rc = mount_get_conns (ctx , cifs_sb , xid , nserver , nses , ntcon );
2989
+ if (* nserver ) {
2990
+ cifs_dbg (FYI , "%s: marking tcp session as a dfs connection\n" , __func__ );
2991
+ spin_lock (& cifs_tcp_ses_lock );
2992
+ (* nserver )-> is_dfs_conn = true;
2993
+ spin_unlock (& cifs_tcp_ses_lock );
2994
+ }
2995
+ return rc ;
2996
+ }
2997
+
2913
2998
/*
2914
2999
* cifs_build_path_to_root returns full path to root when we do not have an
2915
3000
* existing connection (tcon)
@@ -3105,7 +3190,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
3105
3190
tmp_ctx .prepath );
3106
3191
3107
3192
mount_put_conns (cifs_sb , * xid , * server , * ses , * tcon );
3108
- rc = mount_get_conns (& tmp_ctx , cifs_sb , xid , server , ses , tcon );
3193
+ rc = mount_get_dfs_conns (& tmp_ctx , cifs_sb , xid , server , ses , tcon );
3109
3194
if (!rc || (* server && * ses )) {
3110
3195
/*
3111
3196
* We were able to connect to new target server. Update current context with
@@ -3404,7 +3489,12 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
3404
3489
goto error ;
3405
3490
}
3406
3491
3407
- ctx -> nosharesock = true;
3492
+ mount_put_conns (cifs_sb , xid , server , ses , tcon );
3493
+ /*
3494
+ * Ignore error check here because we may failover to other targets from cached a
3495
+ * referral.
3496
+ */
3497
+ (void )mount_get_dfs_conns (ctx , cifs_sb , & xid , & server , & ses , & tcon );
3408
3498
3409
3499
/* Get path of DFS root */
3410
3500
ref_path = build_unc_path_to_root (ctx , cifs_sb , false);
@@ -3433,7 +3523,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
3433
3523
/* Connect to new DFS target only if we were redirected */
3434
3524
if (oldmnt != cifs_sb -> ctx -> mount_options ) {
3435
3525
mount_put_conns (cifs_sb , xid , server , ses , tcon );
3436
- rc = mount_get_conns (ctx , cifs_sb , & xid , & server , & ses , & tcon );
3526
+ rc = mount_get_dfs_conns (ctx , cifs_sb , & xid , & server , & ses , & tcon );
3437
3527
}
3438
3528
if (rc && !server && !ses ) {
3439
3529
/* Failed to connect. Try to connect to other targets in the referral. */
@@ -3459,7 +3549,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
3459
3549
rc = - ELOOP ;
3460
3550
} while (rc == - EREMOTE );
3461
3551
3462
- if (rc || !tcon )
3552
+ if (rc || !tcon || ! ses )
3463
3553
goto error ;
3464
3554
3465
3555
kfree (ref_path );
@@ -4095,7 +4185,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
4095
4185
if (!tree )
4096
4186
return - ENOMEM ;
4097
4187
4098
- if (!tcon -> dfs_path ) {
4188
+ /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
4189
+ if (!tcon -> dfs_path || dfs_cache_noreq_find (tcon -> dfs_path + 1 , & ref , & tl )) {
4099
4190
if (tcon -> ipc ) {
4100
4191
scnprintf (tree , MAX_TREE_SIZE , "\\\\%s\\IPC$" , server -> hostname );
4101
4192
rc = ops -> tree_connect (xid , tcon -> ses , tree , tcon , nlsc );
@@ -4105,9 +4196,6 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
4105
4196
goto out ;
4106
4197
}
4107
4198
4108
- rc = dfs_cache_noreq_find (tcon -> dfs_path + 1 , & ref , & tl );
4109
- if (rc )
4110
- goto out ;
4111
4199
isroot = ref .server_type == DFS_TYPE_ROOT ;
4112
4200
free_dfs_info_param (& ref );
4113
4201
0 commit comments