| From c10cc8cea2527346c3b3019c506124477b751c1a Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 18 Sep 2020 05:37:28 +0000 |
| Subject: Handle STATUS_IO_TIMEOUT gracefully |
| |
| From: Rohith Surabattula <rohiths@microsoft.com> |
| |
| [ Upstream commit 8e670f77c4a55013db6d23b962f9bf6673a5e7b6 ] |
| |
| Currently STATUS_IO_TIMEOUT is not treated as retriable error. |
| It is currently mapped to ETIMEDOUT and returned to userspace |
| for most system calls. STATUS_IO_TIMEOUT is returned by server |
| in case of unavailability or throttling errors. |
| |
| This patch will map the STATUS_IO_TIMEOUT to EAGAIN, so that it |
| can be retried. Also, added a check to drop the connection to |
| not overload the server in case of ongoing unavailability. |
| |
| Signed-off-by: Rohith Surabattula <rohiths@microsoft.com> |
| Reviewed-by: Aurelien Aptel <aaptel@suse.com> |
| Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> |
| Signed-off-by: Steve French <stfrench@microsoft.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/cifs/cifsglob.h | 2 ++ |
| fs/cifs/connect.c | 15 ++++++++++++++- |
| fs/cifs/smb2maperror.c | 2 +- |
| fs/cifs/smb2ops.c | 15 +++++++++++++++ |
| 4 files changed, 32 insertions(+), 2 deletions(-) |
| |
| diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h |
| index b565d83ba89ed..5a491afafacc7 100644 |
| --- a/fs/cifs/cifsglob.h |
| +++ b/fs/cifs/cifsglob.h |
| @@ -510,6 +510,8 @@ struct smb_version_operations { |
| struct fiemap_extent_info *, u64, u64); |
| /* version specific llseek implementation */ |
| loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int); |
| + /* Check for STATUS_IO_TIMEOUT */ |
| + bool (*is_status_io_timeout)(char *buf); |
| }; |
| |
| struct smb_version_values { |
| diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c |
| index 9817a31a39db6..b8780a79a42a2 100644 |
| --- a/fs/cifs/connect.c |
| +++ b/fs/cifs/connect.c |
| @@ -69,6 +69,9 @@ extern bool disable_legacy_dialects; |
| #define TLINK_ERROR_EXPIRE (1 * HZ) |
| #define TLINK_IDLE_EXPIRE (600 * HZ) |
| |
| +/* Drop the connection to not overload the server */ |
| +#define NUM_STATUS_IO_TIMEOUT 5 |
| + |
| enum { |
| /* Mount options that take no arguments */ |
| Opt_user_xattr, Opt_nouser_xattr, |
| @@ -1117,7 +1120,7 @@ cifs_demultiplex_thread(void *p) |
| struct task_struct *task_to_wake = NULL; |
| struct mid_q_entry *mids[MAX_COMPOUND]; |
| char *bufs[MAX_COMPOUND]; |
| - unsigned int noreclaim_flag; |
| + unsigned int noreclaim_flag, num_io_timeout = 0; |
| |
| noreclaim_flag = memalloc_noreclaim_save(); |
| cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); |
| @@ -1213,6 +1216,16 @@ next_pdu: |
| continue; |
| } |
| |
| + if (server->ops->is_status_io_timeout && |
| + server->ops->is_status_io_timeout(buf)) { |
| + num_io_timeout++; |
| + if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) { |
| + cifs_reconnect(server); |
| + num_io_timeout = 0; |
| + continue; |
| + } |
| + } |
| + |
| server->lstrp = jiffies; |
| |
| for (i = 0; i < num_mids; i++) { |
| diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c |
| index 7fde3775cb574..b004cf87692a7 100644 |
| --- a/fs/cifs/smb2maperror.c |
| +++ b/fs/cifs/smb2maperror.c |
| @@ -488,7 +488,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { |
| {STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"}, |
| {STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"}, |
| {STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"}, |
| - {STATUS_IO_TIMEOUT, -ETIMEDOUT, "STATUS_IO_TIMEOUT"}, |
| + {STATUS_IO_TIMEOUT, -EAGAIN, "STATUS_IO_TIMEOUT"}, |
| {STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"}, |
| {STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"}, |
| {STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"}, |
| diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c |
| index 09e1cd320ee56..e2e53652193e6 100644 |
| --- a/fs/cifs/smb2ops.c |
| +++ b/fs/cifs/smb2ops.c |
| @@ -2346,6 +2346,17 @@ smb2_is_session_expired(char *buf) |
| return true; |
| } |
| |
| +static bool |
| +smb2_is_status_io_timeout(char *buf) |
| +{ |
| + struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; |
| + |
| + if (shdr->Status == STATUS_IO_TIMEOUT) |
| + return true; |
| + else |
| + return false; |
| +} |
| + |
| static int |
| smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, |
| struct cifsInodeInfo *cinode) |
| @@ -4816,6 +4827,7 @@ struct smb_version_operations smb20_operations = { |
| .make_node = smb2_make_node, |
| .fiemap = smb3_fiemap, |
| .llseek = smb3_llseek, |
| + .is_status_io_timeout = smb2_is_status_io_timeout, |
| }; |
| |
| struct smb_version_operations smb21_operations = { |
| @@ -4916,6 +4928,7 @@ struct smb_version_operations smb21_operations = { |
| .make_node = smb2_make_node, |
| .fiemap = smb3_fiemap, |
| .llseek = smb3_llseek, |
| + .is_status_io_timeout = smb2_is_status_io_timeout, |
| }; |
| |
| struct smb_version_operations smb30_operations = { |
| @@ -5026,6 +5039,7 @@ struct smb_version_operations smb30_operations = { |
| .make_node = smb2_make_node, |
| .fiemap = smb3_fiemap, |
| .llseek = smb3_llseek, |
| + .is_status_io_timeout = smb2_is_status_io_timeout, |
| }; |
| |
| struct smb_version_operations smb311_operations = { |
| @@ -5137,6 +5151,7 @@ struct smb_version_operations smb311_operations = { |
| .make_node = smb2_make_node, |
| .fiemap = smb3_fiemap, |
| .llseek = smb3_llseek, |
| + .is_status_io_timeout = smb2_is_status_io_timeout, |
| }; |
| |
| struct smb_version_values smb20_values = { |
| -- |
| 2.27.0 |
| |