Commit ca567eb2 authored by Steve French's avatar Steve French

SMB3: Allow persistent handle timeout to be configurable on mount

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: default avatarSteve French <stfrench@microsoft.com>
Reviewed-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
Reviewed-by: default avatarRonnie Sahlberg <lsahlber@redhat.com>
CC: Stable <stable@vger.kernel.org>
parent 153322f7
...@@ -559,6 +559,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) ...@@ -559,6 +559,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
tcon->ses->server->echo_interval / HZ); tcon->ses->server->echo_interval / HZ);
if (tcon->snapshot_time) if (tcon->snapshot_time)
seq_printf(s, ",snapshot=%llu", 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 */ /* convert actimeo and display it in seconds */
seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
......
...@@ -59,6 +59,12 @@ ...@@ -59,6 +59,12 @@
*/ */
#define CIFS_MAX_ACTIMEO (1 << 30) #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 * MAX_REQ is the maximum number of requests that WE will send
* on one socket concurrently. * on one socket concurrently.
...@@ -586,6 +592,7 @@ struct smb_vol { ...@@ -586,6 +592,7 @@ struct smb_vol {
struct nls_table *local_nls; struct nls_table *local_nls;
unsigned int echo_interval; /* echo interval in secs */ unsigned int echo_interval; /* echo interval in secs */
__u64 snapshot_time; /* needed for timewarp tokens */ __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 */ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
}; };
...@@ -1058,6 +1065,7 @@ struct cifs_tcon { ...@@ -1058,6 +1065,7 @@ struct cifs_tcon {
__u32 vol_serial_number; __u32 vol_serial_number;
__le64 vol_create_time; __le64 vol_create_time;
__u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */ __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 ss_flags; /* sector size flags */
__u32 perf_sector_size; /* best sector size for perf */ __u32 perf_sector_size; /* best sector size for perf */
__u32 max_chunks; __u32 max_chunks;
......
...@@ -103,7 +103,7 @@ enum { ...@@ -103,7 +103,7 @@ enum {
Opt_cruid, Opt_gid, Opt_file_mode, Opt_cruid, Opt_gid, Opt_file_mode,
Opt_dirmode, Opt_port, Opt_dirmode, Opt_port,
Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo, Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
Opt_echo_interval, Opt_max_credits, Opt_echo_interval, Opt_max_credits, Opt_handletimeout,
Opt_snapshot, Opt_snapshot,
/* Mount options which take string value */ /* Mount options which take string value */
...@@ -208,6 +208,7 @@ static const match_table_t cifs_mount_option_tokens = { ...@@ -208,6 +208,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_rsize, "rsize=%s" }, { Opt_rsize, "rsize=%s" },
{ Opt_wsize, "wsize=%s" }, { Opt_wsize, "wsize=%s" },
{ Opt_actimeo, "actimeo=%s" }, { Opt_actimeo, "actimeo=%s" },
{ Opt_handletimeout, "handletimeout=%s" },
{ Opt_echo_interval, "echo_interval=%s" }, { Opt_echo_interval, "echo_interval=%s" },
{ Opt_max_credits, "max_credits=%s" }, { Opt_max_credits, "max_credits=%s" },
{ Opt_snapshot, "snapshot=%s" }, { Opt_snapshot, "snapshot=%s" },
...@@ -1619,6 +1620,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1619,6 +1620,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->actimeo = CIFS_DEF_ACTIMEO; 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 */ /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */
vol->ops = &smb30_operations; vol->ops = &smb30_operations;
vol->vals = &smbdefault_values; vol->vals = &smbdefault_values;
...@@ -2017,6 +2021,18 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -2017,6 +2021,18 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
break; 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: case Opt_echo_interval:
if (get_option_ul(args, &option)) { if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "%s: Invalid echo interval value\n", cifs_dbg(VFS, "%s: Invalid echo interval value\n",
...@@ -3183,6 +3199,8 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info) ...@@ -3183,6 +3199,8 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info)
return 0; return 0;
if (tcon->snapshot_time != volume_info->snapshot_time) if (tcon->snapshot_time != volume_info->snapshot_time)
return 0; return 0;
if (tcon->handle_timeout != volume_info->handle_timeout)
return 0;
return 1; return 1;
} }
...@@ -3297,6 +3315,16 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) ...@@ -3297,6 +3315,16 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
tcon->snapshot_time = volume_info->snapshot_time; 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; tcon->ses = ses;
if (volume_info->password) { if (volume_info->password) {
tcon->password = kstrdup(volume_info->password, GFP_KERNEL); tcon->password = kstrdup(volume_info->password, GFP_KERNEL);
......
...@@ -68,7 +68,9 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, ...@@ -68,7 +68,9 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
if (oparms->tcon->use_resilient) { 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; nr_ioctl_req.Reserved = 0;
rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
......
...@@ -1859,8 +1859,9 @@ add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, ...@@ -1859,8 +1859,9 @@ add_lease_context(struct TCP_Server_Info *server, struct kvec *iov,
} }
static struct create_durable_v2 * 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; struct create_durable_v2 *buf;
buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL);
...@@ -1874,7 +1875,14 @@ create_durable_v2_buf(struct cifs_fid *pfid) ...@@ -1874,7 +1875,14 @@ create_durable_v2_buf(struct cifs_fid *pfid)
(struct create_durable_v2, Name)); (struct create_durable_v2, Name));
buf->ccontext.NameLength = cpu_to_le16(4); 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); buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
generate_random_uuid(buf->dcontext.CreateGuid); generate_random_uuid(buf->dcontext.CreateGuid);
memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
...@@ -1927,7 +1935,7 @@ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, ...@@ -1927,7 +1935,7 @@ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec,
struct smb2_create_req *req = iov[0].iov_base; struct smb2_create_req *req = iov[0].iov_base;
unsigned int num = *num_iovec; 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) if (iov[num].iov_base == NULL)
return -ENOMEM; return -ENOMEM;
iov[num].iov_len = sizeof(struct create_durable_v2); iov[num].iov_len = sizeof(struct create_durable_v2);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment