| From 506c1da44fee32ba1d3a70413289ad58c772bba6 Mon Sep 17 00:00:00 2001 |
| From: Shyam Prasad N <sprasad@microsoft.com> |
| Date: Tue, 18 May 2021 15:05:50 +0000 |
| Subject: cifs: use the expiry output of dns_query to schedule next resolution |
| |
| From: Shyam Prasad N <sprasad@microsoft.com> |
| |
| commit 506c1da44fee32ba1d3a70413289ad58c772bba6 upstream. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/cifs/cifs_dfs_ref.c | 2 - |
| fs/cifs/cifsglob.h | 4 +++ |
| fs/cifs/connect.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++--- |
| fs/cifs/dns_resolve.c | 10 +++++--- |
| fs/cifs/dns_resolve.h | 2 - |
| fs/cifs/misc.c | 2 - |
| 6 files changed, 65 insertions(+), 10 deletions(-) |
| |
| --- a/fs/cifs/cifs_dfs_ref.c |
| +++ b/fs/cifs/cifs_dfs_ref.c |
| @@ -173,7 +173,7 @@ char *cifs_compose_mount_options(const c |
| } |
| } |
| |
| - 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); |
| --- a/fs/cifs/cifsglob.h |
| +++ b/fs/cifs/cifsglob.h |
| @@ -84,6 +84,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 |
| |
| @@ -654,6 +657,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 */ |
| --- a/fs/cifs/connect.c |
| +++ b/fs/cifs/connect.c |
| @@ -92,6 +92,8 @@ static int reconn_set_ipaddr_from_hostna |
| 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; |
| @@ -105,13 +107,13 @@ static int reconn_set_ipaddr_from_hostna |
| } |
| 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); |
| @@ -120,7 +122,45 @@ static int reconn_set_ipaddr_from_hostna |
| 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 |
| @@ -720,6 +760,7 @@ static void clean_demultiplex_info(struc |
| 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; |
| @@ -1300,6 +1341,7 @@ cifs_put_tcp_session(struct TCP_Server_I |
| spin_unlock(&cifs_tcp_ses_lock); |
| |
| cancel_delayed_work_sync(&server->echo); |
| + cancel_delayed_work_sync(&server->resolve); |
| |
| if (from_reconnect) |
| /* |
| @@ -1382,6 +1424,7 @@ cifs_get_tcp_session(struct smb3_fs_cont |
| 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, |
| @@ -1462,6 +1505,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: |
| --- a/fs/cifs/dns_resolve.c |
| +++ b/fs/cifs/dns_resolve.c |
| @@ -36,6 +36,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. |
| @@ -43,7 +44,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; |
| @@ -78,13 +79,14 @@ dns_resolve_server_name_to_ip(const char |
| |
| /* 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: |
| --- a/fs/cifs/dns_resolve.h |
| +++ b/fs/cifs/dns_resolve.h |
| @@ -24,7 +24,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 */ |
| --- a/fs/cifs/misc.c |
| +++ b/fs/cifs/misc.c |
| @@ -1105,7 +1105,7 @@ int match_target_ip(struct TCP_Server_In |
| |
| 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; |
| |