mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
cifs: use the expiry output of dns_query to schedule next resolution
We recently fixed DNS resolution of the server hostname during reconnect. However, server IP address may change, even when the old one continues to server (although sub-optimally). We should schedule the next DNS resolution based on the TTL of the DNS record used for the last resolution. This way, we resolve the server hostname again when a DNS record expires. Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz> Cc: <stable@vger.kernel.org> # v5.11+ Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
e73f0f0ee7
commit
506c1da44f
@ -176,7 +176,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
}
|
||||
}
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(name, &srvIP);
|
||||
rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL);
|
||||
if (rc < 0) {
|
||||
cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
|
||||
__func__, name, rc);
|
||||
|
@ -75,6 +75,9 @@
|
||||
#define SMB_ECHO_INTERVAL_MAX 600
|
||||
#define SMB_ECHO_INTERVAL_DEFAULT 60
|
||||
|
||||
/* dns resolution interval in seconds */
|
||||
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600
|
||||
|
||||
/* maximum number of PDUs in one compound */
|
||||
#define MAX_COMPOUND 5
|
||||
|
||||
@ -646,6 +649,7 @@ struct TCP_Server_Info {
|
||||
/* point to the SMBD connection if RDMA is used instead of socket */
|
||||
struct smbd_connection *smbd_conn;
|
||||
struct delayed_work echo; /* echo ping workqueue job */
|
||||
struct delayed_work resolve; /* dns resolution workqueue job */
|
||||
char *smallbuf; /* pointer to current "small" buffer */
|
||||
char *bigbuf; /* pointer to current "big" buffer */
|
||||
/* Total size of this PDU. Only valid from cifs_demultiplex_thread */
|
||||
|
@ -78,6 +78,8 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
|
||||
int rc;
|
||||
int len;
|
||||
char *unc, *ipaddr = NULL;
|
||||
time64_t expiry, now;
|
||||
unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
|
||||
|
||||
if (!server->hostname)
|
||||
return -EINVAL;
|
||||
@ -91,13 +93,13 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
|
||||
}
|
||||
scnprintf(unc, len, "\\\\%s", server->hostname);
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
|
||||
rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry);
|
||||
kfree(unc);
|
||||
|
||||
if (rc < 0) {
|
||||
cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
|
||||
__func__, server->hostname, rc);
|
||||
return rc;
|
||||
goto requeue_resolve;
|
||||
}
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
@ -106,7 +108,45 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
kfree(ipaddr);
|
||||
|
||||
return !rc ? -1 : 0;
|
||||
/* rc == 1 means success here */
|
||||
if (rc) {
|
||||
now = ktime_get_real_seconds();
|
||||
if (expiry && expiry > now)
|
||||
/*
|
||||
* To make sure we don't use the cached entry, retry 1s
|
||||
* after expiry.
|
||||
*/
|
||||
ttl = (expiry - now + 1);
|
||||
}
|
||||
rc = !rc ? -1 : 0;
|
||||
|
||||
requeue_resolve:
|
||||
cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n",
|
||||
__func__, ttl);
|
||||
mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void cifs_resolve_server(struct work_struct *work)
|
||||
{
|
||||
int rc;
|
||||
struct TCP_Server_Info *server = container_of(work,
|
||||
struct TCP_Server_Info, resolve.work);
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
|
||||
/*
|
||||
* Resolve the hostname again to make sure that IP address is up-to-date.
|
||||
*/
|
||||
rc = reconn_set_ipaddr_from_hostname(server);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
@ -680,6 +720,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cancel_delayed_work_sync(&server->echo);
|
||||
cancel_delayed_work_sync(&server->resolve);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
server->tcpStatus = CifsExiting;
|
||||
@ -1260,6 +1301,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cancel_delayed_work_sync(&server->echo);
|
||||
cancel_delayed_work_sync(&server->resolve);
|
||||
|
||||
if (from_reconnect)
|
||||
/*
|
||||
@ -1342,6 +1384,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
|
||||
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
|
||||
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
|
||||
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
|
||||
INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server);
|
||||
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
|
||||
mutex_init(&tcp_ses->reconnect_mutex);
|
||||
memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
|
||||
@ -1427,6 +1470,12 @@ smbd_connected:
|
||||
/* queue echo request delayed work */
|
||||
queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
|
||||
|
||||
/* queue dns resolution delayed work */
|
||||
cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n",
|
||||
__func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT);
|
||||
|
||||
queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ));
|
||||
|
||||
return tcp_ses;
|
||||
|
||||
out_err_crypto_release:
|
||||
|
@ -24,6 +24,7 @@
|
||||
* dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
|
||||
* @unc: UNC path specifying the server (with '/' as delimiter)
|
||||
* @ip_addr: Where to return the IP address.
|
||||
* @expiry: Where to return the expiry time for the dns record.
|
||||
*
|
||||
* The IP address will be returned in string form, and the caller is
|
||||
* responsible for freeing it.
|
||||
@ -31,7 +32,7 @@
|
||||
* Returns length of result on success, -ve on error.
|
||||
*/
|
||||
int
|
||||
dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
|
||||
dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
const char *hostname, *sep;
|
||||
@ -66,13 +67,14 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
|
||||
|
||||
/* Perform the upcall */
|
||||
rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
|
||||
NULL, ip_addr, NULL, false);
|
||||
NULL, ip_addr, expiry, false);
|
||||
if (rc < 0)
|
||||
cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
|
||||
__func__, len, len, hostname);
|
||||
else
|
||||
cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
|
||||
__func__, len, len, hostname, *ip_addr);
|
||||
cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
|
||||
__func__, len, len, hostname, *ip_addr,
|
||||
expiry ? (*expiry) : 0);
|
||||
return rc;
|
||||
|
||||
name_is_IP_address:
|
||||
|
@ -12,7 +12,7 @@
|
||||
#define _DNS_RESOLVE_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
|
||||
extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry);
|
||||
#endif /* KERNEL */
|
||||
|
||||
#endif /* _DNS_RESOLVE_H */
|
||||
|
@ -1187,7 +1187,7 @@ int match_target_ip(struct TCP_Server_Info *server,
|
||||
|
||||
cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(target, &tip);
|
||||
rc = dns_resolve_server_name_to_ip(target, &tip, NULL);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user