diff mbox series

cifs: introduce dns_interval mount option

Message ID 20220609224342.892-1-ematsumiya@suse.de
State New
Headers show
Series cifs: introduce dns_interval mount option | expand

Commit Message

Enzo Matsumiya June 9, 2022, 10:43 p.m. UTC
This patch introduces a `dns_interval' mount option, used to configure
the interval that the DNS resolve worker should be run.

Enforces the minimum value SMB_DNS_RESOLVE_INTERVAL_MIN (currently 120s),
or uses the default SMB_DNS_RESOLVE_INTERVAL_DEFAULT (currently 600s).

Since this is a mount option, each derived connection from it, e.g. DFS
root targets, will share the same DNS interval from the primary server
since the TCP session options are passed down to them.

Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
---
 fs/cifs/cifsfs.c     |  3 +++
 fs/cifs/cifsglob.h   |  1 +
 fs/cifs/connect.c    | 20 ++++++++++++++------
 fs/cifs/fs_context.c | 11 +++++++++++
 fs/cifs/fs_context.h |  2 ++
 fs/cifs/sess.c       |  1 +
 6 files changed, 32 insertions(+), 6 deletions(-)

Comments

Shyam Prasad N June 10, 2022, 6:50 a.m. UTC | #1
On Fri, Jun 10, 2022 at 4:14 AM Enzo Matsumiya <ematsumiya@suse.de> wrote:
>
> This patch introduces a `dns_interval' mount option, used to configure
> the interval that the DNS resolve worker should be run.
>
> Enforces the minimum value SMB_DNS_RESOLVE_INTERVAL_MIN (currently 120s),
> or uses the default SMB_DNS_RESOLVE_INTERVAL_DEFAULT (currently 600s).
>
> Since this is a mount option, each derived connection from it, e.g. DFS
> root targets, will share the same DNS interval from the primary server
> since the TCP session options are passed down to them.
>
> Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
> ---
>  fs/cifs/cifsfs.c     |  3 +++
>  fs/cifs/cifsglob.h   |  1 +
>  fs/cifs/connect.c    | 20 ++++++++++++++------
>  fs/cifs/fs_context.c | 11 +++++++++++
>  fs/cifs/fs_context.h |  2 ++
>  fs/cifs/sess.c       |  1 +
>  6 files changed, 32 insertions(+), 6 deletions(-)
>
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index 325423180fd2..ad980b235699 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -665,6 +665,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
>         if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE)
>                 seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits);
>
> +       if (tcon->ses->server->dns_interval != SMB_DNS_RESOLVE_INTERVAL_DEFAULT)
> +               seq_printf(s, ",dns_interval=%u", tcon->ses->server->dns_interval);
> +
>         if (tcon->snapshot_time)
>                 seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
>         if (tcon->handle_timeout)
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index f873379066c7..e28a23b617ef 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -679,6 +679,7 @@ struct TCP_Server_Info {
>         struct smbd_connection *smbd_conn;
>         struct delayed_work     echo; /* echo ping workqueue job */
>         struct delayed_work     resolve; /* dns resolution workqueue job */
> +       unsigned int dns_interval; /* interval for resolve worker */
>         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 */
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 06bafba9c3ff..e6bedced576a 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -92,7 +92,7 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
>         int len;
>         char *unc, *ipaddr = NULL;
>         time64_t expiry, now;
> -       unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
> +       unsigned int ttl = server->dns_interval;
>
>         if (!server->hostname ||
>             server->hostname[0] == '\0')
> @@ -129,13 +129,15 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
>                         /*
>                          * To make sure we don't use the cached entry, retry 1s
>                          * after expiry.
> +                        *
> +                        * dns_interval is guaranteed to be >= SMB_DNS_RESOLVE_INTERVAL_MIN
>                          */
> -                       ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1;
> +                       ttl = max_t(unsigned long, expiry - now, server->dns_interval) + 1;
>         }
>         rc = !rc ? -1 : 0;
>
>  requeue_resolve:
> -       cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n",
> +       cifs_dbg(FYI, "%s: next dns resolution scheduled for %u seconds in the future\n",
>                  __func__, ttl);
>         mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
>
> @@ -1608,6 +1610,12 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
>                 tcp_ses->echo_interval = ctx->echo_interval * HZ;
>         else
>                 tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
> +
> +       if (ctx->dns_interval >= SMB_DNS_RESOLVE_INTERVAL_MIN)
> +               tcp_ses->dns_interval = ctx->dns_interval;
> +       else
> +               tcp_ses->dns_interval = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
> +
Hi Enzo,

Is the above line a mistake? Shouldn't that be set to
SMB_DNS_RESOLVE_INTERVAL_MIN value in the else case?
Rest looks good to me. You can add my RB.

>         if (tcp_ses->rdma) {
>  #ifndef CONFIG_CIFS_SMB_DIRECT
>                 cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
> @@ -1670,10 +1678,10 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
>         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);
> +       cifs_dbg(FYI, "%s: next dns resolution scheduled for %u seconds in the future\n",
> +                __func__, tcp_ses->dns_interval);
>
> -       queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ));
> +       queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (tcp_ses->dns_interval * HZ));
>
>         return tcp_ses;
>
> diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c
> index 8dc0d923ef6a..91b3424ba722 100644
> --- a/fs/cifs/fs_context.c
> +++ b/fs/cifs/fs_context.c
> @@ -152,6 +152,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
>         fsparam_u32("handletimeout", Opt_handletimeout),
>         fsparam_u64("snapshot", Opt_snapshot),
>         fsparam_u32("max_channels", Opt_max_channels),
> +       fsparam_u32("dns_interval", Opt_dns_interval),
>
>         /* Mount options which take string value */
>         fsparam_string("source", Opt_source),
> @@ -1099,6 +1100,14 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
>                 if (result.uint_32 > 1)
>                         ctx->multichannel = true;
>                 break;
> +       case Opt_dns_interval:
> +               if (result.uint_32 < SMB_DNS_RESOLVE_INTERVAL_MIN) {
> +                       cifs_errorf(fc, "%s: Minimum value for dns_interval is %u\n",
> +                                       __func__, SMB_DNS_RESOLVE_INTERVAL_MIN);
> +                       goto cifs_parse_mount_err;
> +               }
> +               ctx->dns_interval = result.uint_32;
> +               break;
>         case Opt_handletimeout:
>                 ctx->handle_timeout = result.uint_32;
>                 if (ctx->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) {
> @@ -1535,6 +1544,8 @@ int smb3_init_fs_context(struct fs_context *fc)
>         ctx->multichannel = false;
>         ctx->max_channels = 1;
>
> +       ctx->dns_interval = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
> +
>         ctx->backupuid_specified = false; /* no backup intent for a user */
>         ctx->backupgid_specified = false; /* no backup intent for a group */
>
> diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h
> index 5f093cb7e9b9..567a2dde6333 100644
> --- a/fs/cifs/fs_context.h
> +++ b/fs/cifs/fs_context.h
> @@ -130,6 +130,7 @@ enum cifs_param {
>         Opt_snapshot,
>         Opt_max_channels,
>         Opt_handletimeout,
> +       Opt_dns_interval,
>
>         /* Mount options which take string value */
>         Opt_source,
> @@ -258,6 +259,7 @@ struct smb3_fs_context {
>         __u32 handle_timeout; /* persistent and durable handle timeout in ms */
>         unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
>         unsigned int max_channels;
> +       unsigned int dns_interval; /* interval to resolve server hostname */
>         __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
>         bool rootfs:1; /* if it's a SMB root file system */
>         bool witness:1; /* use witness protocol */
> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
> index 0bece97547d4..d3dad612e2a4 100644
> --- a/fs/cifs/sess.c
> +++ b/fs/cifs/sess.c
> @@ -325,6 +325,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
>         ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
>         ctx.echo_interval = ses->server->echo_interval / HZ;
>         ctx.max_credits = ses->server->max_credits;
> +       ctx.dns_interval = ses->server->dns_interval;
>
>         /*
>          * This will be used for encoding/decoding user/domain/pw
> --
> 2.36.1
>
Enzo Matsumiya June 10, 2022, 5:16 p.m. UTC | #2
On 06/10, Shyam Prasad N wrote:
>On Fri, Jun 10, 2022 at 4:14 AM Enzo Matsumiya <ematsumiya@suse.de> wrote:
>>
>> This patch introduces a `dns_interval' mount option, used to configure
>> the interval that the DNS resolve worker should be run.
>>
>> Enforces the minimum value SMB_DNS_RESOLVE_INTERVAL_MIN (currently 120s),
>> or uses the default SMB_DNS_RESOLVE_INTERVAL_DEFAULT (currently 600s).
>>
>> Since this is a mount option, each derived connection from it, e.g. DFS
>> root targets, will share the same DNS interval from the primary server
>> since the TCP session options are passed down to them.
>>
>> Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
>> ---
>>  fs/cifs/cifsfs.c     |  3 +++
>>  fs/cifs/cifsglob.h   |  1 +
>>  fs/cifs/connect.c    | 20 ++++++++++++++------
>>  fs/cifs/fs_context.c | 11 +++++++++++
>>  fs/cifs/fs_context.h |  2 ++
>>  fs/cifs/sess.c       |  1 +
>>  6 files changed, 32 insertions(+), 6 deletions(-)
>>
>> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
>> index 325423180fd2..ad980b235699 100644
>> --- a/fs/cifs/cifsfs.c
>> +++ b/fs/cifs/cifsfs.c
>> @@ -665,6 +665,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
>>         if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE)
>>                 seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits);
>>
>> +       if (tcon->ses->server->dns_interval != SMB_DNS_RESOLVE_INTERVAL_DEFAULT)
>> +               seq_printf(s, ",dns_interval=%u", tcon->ses->server->dns_interval);
>> +
>>         if (tcon->snapshot_time)
>>                 seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
>>         if (tcon->handle_timeout)
>> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
>> index f873379066c7..e28a23b617ef 100644
>> --- a/fs/cifs/cifsglob.h
>> +++ b/fs/cifs/cifsglob.h
>> @@ -679,6 +679,7 @@ struct TCP_Server_Info {
>>         struct smbd_connection *smbd_conn;
>>         struct delayed_work     echo; /* echo ping workqueue job */
>>         struct delayed_work     resolve; /* dns resolution workqueue job */
>> +       unsigned int dns_interval; /* interval for resolve worker */
>>         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 */
>> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
>> index 06bafba9c3ff..e6bedced576a 100644
>> --- a/fs/cifs/connect.c
>> +++ b/fs/cifs/connect.c
>> @@ -92,7 +92,7 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
>>         int len;
>>         char *unc, *ipaddr = NULL;
>>         time64_t expiry, now;
>> -       unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
>> +       unsigned int ttl = server->dns_interval;
>>
>>         if (!server->hostname ||
>>             server->hostname[0] == '\0')
>> @@ -129,13 +129,15 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
>>                         /*
>>                          * To make sure we don't use the cached entry, retry 1s
>>                          * after expiry.
>> +                        *
>> +                        * dns_interval is guaranteed to be >= SMB_DNS_RESOLVE_INTERVAL_MIN
>>                          */
>> -                       ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1;
>> +                       ttl = max_t(unsigned long, expiry - now, server->dns_interval) + 1;
>>         }
>>         rc = !rc ? -1 : 0;
>>
>>  requeue_resolve:
>> -       cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n",
>> +       cifs_dbg(FYI, "%s: next dns resolution scheduled for %u seconds in the future\n",
>>                  __func__, ttl);
>>         mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
>>
>> @@ -1608,6 +1610,12 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
>>                 tcp_ses->echo_interval = ctx->echo_interval * HZ;
>>         else
>>                 tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
>> +
>> +       if (ctx->dns_interval >= SMB_DNS_RESOLVE_INTERVAL_MIN)
>> +               tcp_ses->dns_interval = ctx->dns_interval;
>> +       else
>> +               tcp_ses->dns_interval = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
>> +
>Hi Enzo,
>
>Is the above line a mistake? Shouldn't that be set to
>SMB_DNS_RESOLVE_INTERVAL_MIN value in the else case?
>Rest looks good to me. You can add my RB.

Hy Shyam,

SMB_DNS_RESOLVE_INTERVAL_MIN is just for boundary-checking. In case
dns_interval is < than that, we fallback to the default value (I've copied
echo_interval behaviour, and also the current behaviour).

IMHO we shouldn't use SMB_DNS_RESOLVE_INTERVAL_MIN as a fallback value
because it's too far from the default values used by the servers I've
checked (Windows Server 2019, 600s, samba "name cache timeout" = 660s).


Thanks,

Enzo
Tom Talpey June 13, 2022, 5:16 p.m. UTC | #3
This change looks good to me. I'm not usually a fan of mount
options, but it's entirely logical here, and the option can
easily be no-oped or tweaked in the future in compatible ways
when the upcall is fully implemented.

Reviewed-by: Tom Talpey <tom@talpey.com>

On 6/10/2022 1:16 PM, Enzo Matsumiya wrote:
> On 06/10, Shyam Prasad N wrote:
>> On Fri, Jun 10, 2022 at 4:14 AM Enzo Matsumiya <ematsumiya@suse.de> 
>> wrote:
>>>
>>> This patch introduces a `dns_interval' mount option, used to configure
>>> the interval that the DNS resolve worker should be run.
>>>
>>> Enforces the minimum value SMB_DNS_RESOLVE_INTERVAL_MIN (currently 
>>> 120s),
>>> or uses the default SMB_DNS_RESOLVE_INTERVAL_DEFAULT (currently 600s).
>>>
>>> Since this is a mount option, each derived connection from it, e.g. DFS
>>> root targets, will share the same DNS interval from the primary server
>>> since the TCP session options are passed down to them.
>>>
>>> Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
>>> ---
>>>  fs/cifs/cifsfs.c     |  3 +++
>>>  fs/cifs/cifsglob.h   |  1 +
>>>  fs/cifs/connect.c    | 20 ++++++++++++++------
>>>  fs/cifs/fs_context.c | 11 +++++++++++
>>>  fs/cifs/fs_context.h |  2 ++
>>>  fs/cifs/sess.c       |  1 +
>>>  6 files changed, 32 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
>>> index 325423180fd2..ad980b235699 100644
>>> --- a/fs/cifs/cifsfs.c
>>> +++ b/fs/cifs/cifsfs.c
>>> @@ -665,6 +665,9 @@ cifs_show_options(struct seq_file *s, struct 
>>> dentry *root)
>>>         if (tcon->ses->server->max_credits != 
>>> SMB2_MAX_CREDITS_AVAILABLE)
>>>                 seq_printf(s, ",max_credits=%u", 
>>> tcon->ses->server->max_credits);
>>>
>>> +       if (tcon->ses->server->dns_interval != 
>>> SMB_DNS_RESOLVE_INTERVAL_DEFAULT)
>>> +               seq_printf(s, ",dns_interval=%u", 
>>> tcon->ses->server->dns_interval);
>>> +
>>>         if (tcon->snapshot_time)
>>>                 seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
>>>         if (tcon->handle_timeout)
>>> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
>>> index f873379066c7..e28a23b617ef 100644
>>> --- a/fs/cifs/cifsglob.h
>>> +++ b/fs/cifs/cifsglob.h
>>> @@ -679,6 +679,7 @@ struct TCP_Server_Info {
>>>         struct smbd_connection *smbd_conn;
>>>         struct delayed_work     echo; /* echo ping workqueue job */
>>>         struct delayed_work     resolve; /* dns resolution workqueue 
>>> job */
>>> +       unsigned int dns_interval; /* interval for resolve worker */
>>>         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 */
>>> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
>>> index 06bafba9c3ff..e6bedced576a 100644
>>> --- a/fs/cifs/connect.c
>>> +++ b/fs/cifs/connect.c
>>> @@ -92,7 +92,7 @@ static int reconn_set_ipaddr_from_hostname(struct 
>>> TCP_Server_Info *server)
>>>         int len;
>>>         char *unc, *ipaddr = NULL;
>>>         time64_t expiry, now;
>>> -       unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
>>> +       unsigned int ttl = server->dns_interval;
>>>
>>>         if (!server->hostname ||
>>>             server->hostname[0] == '\0')
>>> @@ -129,13 +129,15 @@ static int 
>>> reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
>>>                         /*
>>>                          * To make sure we don't use the cached 
>>> entry, retry 1s
>>>                          * after expiry.
>>> +                        *
>>> +                        * dns_interval is guaranteed to be >= 
>>> SMB_DNS_RESOLVE_INTERVAL_MIN
>>>                          */
>>> -                       ttl = max_t(unsigned long, expiry - now, 
>>> SMB_DNS_RESOLVE_INTERVAL_MIN) + 1;
>>> +                       ttl = max_t(unsigned long, expiry - now, 
>>> server->dns_interval) + 1;
>>>         }
>>>         rc = !rc ? -1 : 0;
>>>
>>>  requeue_resolve:
>>> -       cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu 
>>> seconds in the future\n",
>>> +       cifs_dbg(FYI, "%s: next dns resolution scheduled for %u 
>>> seconds in the future\n",
>>>                  __func__, ttl);
>>>         mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
>>>
>>> @@ -1608,6 +1610,12 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
>>>                 tcp_ses->echo_interval = ctx->echo_interval * HZ;
>>>         else
>>>                 tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
>>> +
>>> +       if (ctx->dns_interval >= SMB_DNS_RESOLVE_INTERVAL_MIN)
>>> +               tcp_ses->dns_interval = ctx->dns_interval;
>>> +       else
>>> +               tcp_ses->dns_interval = 
>>> SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
>>> +
>> Hi Enzo,
>>
>> Is the above line a mistake? Shouldn't that be set to
>> SMB_DNS_RESOLVE_INTERVAL_MIN value in the else case?
>> Rest looks good to me. You can add my RB.
> 
> Hy Shyam,
> 
> SMB_DNS_RESOLVE_INTERVAL_MIN is just for boundary-checking. In case
> dns_interval is < than that, we fallback to the default value (I've copied
> echo_interval behaviour, and also the current behaviour).
> 
> IMHO we shouldn't use SMB_DNS_RESOLVE_INTERVAL_MIN as a fallback value
> because it's too far from the default values used by the servers I've
> checked (Windows Server 2019, 600s, samba "name cache timeout" = 660s).
> 
> 
> Thanks,
> 
> Enzo
>
diff mbox series

Patch

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 325423180fd2..ad980b235699 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -665,6 +665,9 @@  cifs_show_options(struct seq_file *s, struct dentry *root)
 	if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE)
 		seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits);
 
+	if (tcon->ses->server->dns_interval != SMB_DNS_RESOLVE_INTERVAL_DEFAULT)
+		seq_printf(s, ",dns_interval=%u", tcon->ses->server->dns_interval);
+
 	if (tcon->snapshot_time)
 		seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
 	if (tcon->handle_timeout)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index f873379066c7..e28a23b617ef 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -679,6 +679,7 @@  struct TCP_Server_Info {
 	struct smbd_connection *smbd_conn;
 	struct delayed_work	echo; /* echo ping workqueue job */
 	struct delayed_work	resolve; /* dns resolution workqueue job */
+	unsigned int dns_interval; /* interval for resolve worker */
 	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 */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 06bafba9c3ff..e6bedced576a 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -92,7 +92,7 @@  static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
 	int len;
 	char *unc, *ipaddr = NULL;
 	time64_t expiry, now;
-	unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
+	unsigned int ttl = server->dns_interval;
 
 	if (!server->hostname ||
 	    server->hostname[0] == '\0')
@@ -129,13 +129,15 @@  static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
 			/*
 			 * To make sure we don't use the cached entry, retry 1s
 			 * after expiry.
+			 *
+			 * dns_interval is guaranteed to be >= SMB_DNS_RESOLVE_INTERVAL_MIN
 			 */
-			ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1;
+			ttl = max_t(unsigned long, expiry - now, server->dns_interval) + 1;
 	}
 	rc = !rc ? -1 : 0;
 
 requeue_resolve:
-	cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n",
+	cifs_dbg(FYI, "%s: next dns resolution scheduled for %u seconds in the future\n",
 		 __func__, ttl);
 	mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
 
@@ -1608,6 +1610,12 @@  cifs_get_tcp_session(struct smb3_fs_context *ctx,
 		tcp_ses->echo_interval = ctx->echo_interval * HZ;
 	else
 		tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
+
+	if (ctx->dns_interval >= SMB_DNS_RESOLVE_INTERVAL_MIN)
+		tcp_ses->dns_interval = ctx->dns_interval;
+	else
+		tcp_ses->dns_interval = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
+
 	if (tcp_ses->rdma) {
 #ifndef CONFIG_CIFS_SMB_DIRECT
 		cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
@@ -1670,10 +1678,10 @@  cifs_get_tcp_session(struct smb3_fs_context *ctx,
 	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);
+	cifs_dbg(FYI, "%s: next dns resolution scheduled for %u seconds in the future\n",
+		 __func__, tcp_ses->dns_interval);
 
-	queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ));
+	queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (tcp_ses->dns_interval * HZ));
 
 	return tcp_ses;
 
diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c
index 8dc0d923ef6a..91b3424ba722 100644
--- a/fs/cifs/fs_context.c
+++ b/fs/cifs/fs_context.c
@@ -152,6 +152,7 @@  const struct fs_parameter_spec smb3_fs_parameters[] = {
 	fsparam_u32("handletimeout", Opt_handletimeout),
 	fsparam_u64("snapshot", Opt_snapshot),
 	fsparam_u32("max_channels", Opt_max_channels),
+	fsparam_u32("dns_interval", Opt_dns_interval),
 
 	/* Mount options which take string value */
 	fsparam_string("source", Opt_source),
@@ -1099,6 +1100,14 @@  static int smb3_fs_context_parse_param(struct fs_context *fc,
 		if (result.uint_32 > 1)
 			ctx->multichannel = true;
 		break;
+	case Opt_dns_interval:
+		if (result.uint_32 < SMB_DNS_RESOLVE_INTERVAL_MIN) {
+			cifs_errorf(fc, "%s: Minimum value for dns_interval is %u\n",
+					__func__, SMB_DNS_RESOLVE_INTERVAL_MIN);
+			goto cifs_parse_mount_err;
+		}
+		ctx->dns_interval = result.uint_32;
+		break;
 	case Opt_handletimeout:
 		ctx->handle_timeout = result.uint_32;
 		if (ctx->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) {
@@ -1535,6 +1544,8 @@  int smb3_init_fs_context(struct fs_context *fc)
 	ctx->multichannel = false;
 	ctx->max_channels = 1;
 
+	ctx->dns_interval = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
+
 	ctx->backupuid_specified = false; /* no backup intent for a user */
 	ctx->backupgid_specified = false; /* no backup intent for a group */
 
diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h
index 5f093cb7e9b9..567a2dde6333 100644
--- a/fs/cifs/fs_context.h
+++ b/fs/cifs/fs_context.h
@@ -130,6 +130,7 @@  enum cifs_param {
 	Opt_snapshot,
 	Opt_max_channels,
 	Opt_handletimeout,
+	Opt_dns_interval,
 
 	/* Mount options which take string value */
 	Opt_source,
@@ -258,6 +259,7 @@  struct smb3_fs_context {
 	__u32 handle_timeout; /* persistent and durable handle timeout in ms */
 	unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
 	unsigned int max_channels;
+	unsigned int dns_interval; /* interval to resolve server hostname */
 	__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
 	bool rootfs:1; /* if it's a SMB root file system */
 	bool witness:1; /* use witness protocol */
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 0bece97547d4..d3dad612e2a4 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -325,6 +325,7 @@  cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
 	ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
 	ctx.echo_interval = ses->server->echo_interval / HZ;
 	ctx.max_credits = ses->server->max_credits;
+	ctx.dns_interval = ses->server->dns_interval;
 
 	/*
 	 * This will be used for encoding/decoding user/domain/pw