| From ca567eb2b3f014d5be0f44c6f68b01a522f15ca4 Mon Sep 17 00:00:00 2001 |
| From: Steve French <stfrench@microsoft.com> |
| Date: Fri, 29 Mar 2019 16:31:07 -0500 |
| Subject: SMB3: Allow persistent handle timeout to be configurable on mount |
| |
| From: Steve French <stfrench@microsoft.com> |
| |
| commit ca567eb2b3f014d5be0f44c6f68b01a522f15ca4 upstream. |
| |
| Reconnecting after server or network failure can be improved |
| (to maintain availability and protect data integrity) by allowing |
| the client to choose the default persistent (or resilient) |
| handle timeout in some use cases. Today we default to 0 which lets |
| the server pick the default timeout (usually 120 seconds) but this |
| can be problematic for some workloads. Add the new mount parameter |
| to cifs.ko for SMB3 mounts "handletimeout" which enables the user |
| to override the default handle timeout for persistent (mount |
| option "persistenthandles") or resilient handles (mount option |
| "resilienthandles"). Maximum allowed is 16 minutes (960000 ms). |
| Units for the timeout are expressed in milliseconds. See |
| section 2.2.14.2.12 and 2.2.31.3 of the MS-SMB2 protocol |
| specification for more information. |
| |
| Signed-off-by: Steve French <stfrench@microsoft.com> |
| Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> |
| Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com> |
| CC: Stable <stable@vger.kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/cifs/cifsfs.c | 2 ++ |
| fs/cifs/cifsglob.h | 8 ++++++++ |
| fs/cifs/connect.c | 30 +++++++++++++++++++++++++++++- |
| fs/cifs/smb2file.c | 4 +++- |
| fs/cifs/smb2pdu.c | 14 +++++++++++--- |
| 5 files changed, 53 insertions(+), 5 deletions(-) |
| |
| --- a/fs/cifs/cifsfs.c |
| +++ b/fs/cifs/cifsfs.c |
| @@ -559,6 +559,8 @@ cifs_show_options(struct seq_file *s, st |
| tcon->ses->server->echo_interval / HZ); |
| if (tcon->snapshot_time) |
| seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); |
| + if (tcon->handle_timeout) |
| + seq_printf(s, ",handletimeout=%u", tcon->handle_timeout); |
| /* convert actimeo and display it in seconds */ |
| seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); |
| |
| --- a/fs/cifs/cifsglob.h |
| +++ b/fs/cifs/cifsglob.h |
| @@ -60,6 +60,12 @@ |
| #define CIFS_MAX_ACTIMEO (1 << 30) |
| |
| /* |
| + * Max persistent and resilient handle timeout (milliseconds). |
| + * Windows durable max was 960000 (16 minutes) |
| + */ |
| +#define SMB3_MAX_HANDLE_TIMEOUT 960000 |
| + |
| +/* |
| * MAX_REQ is the maximum number of requests that WE will send |
| * on one socket concurrently. |
| */ |
| @@ -572,6 +578,7 @@ struct smb_vol { |
| struct nls_table *local_nls; |
| unsigned int echo_interval; /* echo interval in secs */ |
| __u64 snapshot_time; /* needed for timewarp tokens */ |
| + __u32 handle_timeout; /* persistent and durable handle timeout in ms */ |
| unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ |
| }; |
| |
| @@ -1028,6 +1035,7 @@ struct cifs_tcon { |
| __u32 vol_serial_number; |
| __le64 vol_create_time; |
| __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */ |
| + __u32 handle_timeout; /* persistent and durable handle timeout in ms */ |
| __u32 ss_flags; /* sector size flags */ |
| __u32 perf_sector_size; /* best sector size for perf */ |
| __u32 max_chunks; |
| --- a/fs/cifs/connect.c |
| +++ b/fs/cifs/connect.c |
| @@ -103,7 +103,7 @@ enum { |
| Opt_cruid, Opt_gid, Opt_file_mode, |
| Opt_dirmode, Opt_port, |
| Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo, |
| - Opt_echo_interval, Opt_max_credits, |
| + Opt_echo_interval, Opt_max_credits, Opt_handletimeout, |
| Opt_snapshot, |
| |
| /* Mount options which take string value */ |
| @@ -208,6 +208,7 @@ static const match_table_t cifs_mount_op |
| { Opt_rsize, "rsize=%s" }, |
| { Opt_wsize, "wsize=%s" }, |
| { Opt_actimeo, "actimeo=%s" }, |
| + { Opt_handletimeout, "handletimeout=%s" }, |
| { Opt_echo_interval, "echo_interval=%s" }, |
| { Opt_max_credits, "max_credits=%s" }, |
| { Opt_snapshot, "snapshot=%s" }, |
| @@ -1600,6 +1601,9 @@ cifs_parse_mount_options(const char *mou |
| |
| vol->actimeo = CIFS_DEF_ACTIMEO; |
| |
| + /* Most clients set timeout to 0, allows server to use its default */ |
| + vol->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */ |
| + |
| /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */ |
| vol->ops = &smb30_operations; |
| vol->vals = &smbdefault_values; |
| @@ -1998,6 +2002,18 @@ cifs_parse_mount_options(const char *mou |
| goto cifs_parse_mount_err; |
| } |
| break; |
| + case Opt_handletimeout: |
| + if (get_option_ul(args, &option)) { |
| + cifs_dbg(VFS, "%s: Invalid handletimeout value\n", |
| + __func__); |
| + goto cifs_parse_mount_err; |
| + } |
| + vol->handle_timeout = option; |
| + if (vol->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) { |
| + cifs_dbg(VFS, "Invalid handle cache timeout, longer than 16 minutes\n"); |
| + goto cifs_parse_mount_err; |
| + } |
| + break; |
| case Opt_echo_interval: |
| if (get_option_ul(args, &option)) { |
| cifs_dbg(VFS, "%s: Invalid echo interval value\n", |
| @@ -3164,6 +3180,8 @@ static int match_tcon(struct cifs_tcon * |
| return 0; |
| if (tcon->snapshot_time != volume_info->snapshot_time) |
| return 0; |
| + if (tcon->handle_timeout != volume_info->handle_timeout) |
| + return 0; |
| return 1; |
| } |
| |
| @@ -3278,6 +3296,16 @@ cifs_get_tcon(struct cifs_ses *ses, stru |
| tcon->snapshot_time = volume_info->snapshot_time; |
| } |
| |
| + if (volume_info->handle_timeout) { |
| + if (ses->server->vals->protocol_id == 0) { |
| + cifs_dbg(VFS, |
| + "Use SMB2.1 or later for handle timeout option\n"); |
| + rc = -EOPNOTSUPP; |
| + goto out_fail; |
| + } else |
| + tcon->handle_timeout = volume_info->handle_timeout; |
| + } |
| + |
| tcon->ses = ses; |
| if (volume_info->password) { |
| tcon->password = kstrdup(volume_info->password, GFP_KERNEL); |
| --- a/fs/cifs/smb2file.c |
| +++ b/fs/cifs/smb2file.c |
| @@ -68,7 +68,9 @@ smb2_open_file(const unsigned int xid, s |
| |
| |
| if (oparms->tcon->use_resilient) { |
| - nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */ |
| + /* default timeout is 0, servers pick default (120 seconds) */ |
| + nr_ioctl_req.Timeout = |
| + cpu_to_le32(oparms->tcon->handle_timeout); |
| nr_ioctl_req.Reserved = 0; |
| rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, |
| fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, |
| --- a/fs/cifs/smb2pdu.c |
| +++ b/fs/cifs/smb2pdu.c |
| @@ -1837,8 +1837,9 @@ add_lease_context(struct TCP_Server_Info |
| } |
| |
| static struct create_durable_v2 * |
| -create_durable_v2_buf(struct cifs_fid *pfid) |
| +create_durable_v2_buf(struct cifs_open_parms *oparms) |
| { |
| + struct cifs_fid *pfid = oparms->fid; |
| struct create_durable_v2 *buf; |
| |
| buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); |
| @@ -1852,7 +1853,14 @@ create_durable_v2_buf(struct cifs_fid *p |
| (struct create_durable_v2, Name)); |
| buf->ccontext.NameLength = cpu_to_le16(4); |
| |
| - buf->dcontext.Timeout = 0; /* Should this be configurable by workload */ |
| + /* |
| + * NB: Handle timeout defaults to 0, which allows server to choose |
| + * (most servers default to 120 seconds) and most clients default to 0. |
| + * This can be overridden at mount ("handletimeout=") if the user wants |
| + * a different persistent (or resilient) handle timeout for all opens |
| + * opens on a particular SMB3 mount. |
| + */ |
| + buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); |
| buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); |
| generate_random_uuid(buf->dcontext.CreateGuid); |
| memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); |
| @@ -1905,7 +1913,7 @@ add_durable_v2_context(struct kvec *iov, |
| struct smb2_create_req *req = iov[0].iov_base; |
| unsigned int num = *num_iovec; |
| |
| - iov[num].iov_base = create_durable_v2_buf(oparms->fid); |
| + iov[num].iov_base = create_durable_v2_buf(oparms); |
| if (iov[num].iov_base == NULL) |
| return -ENOMEM; |
| iov[num].iov_len = sizeof(struct create_durable_v2); |