]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
smb: client: Store original IO parameters and prevent zero IO sizes
authorWang Zhaolong <wangzhaolong1@huawei.com>
Mon, 31 Mar 2025 13:33:14 +0000 (21:33 +0800)
committerSteve French <stfrench@microsoft.com>
Tue, 1 Apr 2025 02:12:31 +0000 (21:12 -0500)
During mount option processing and negotiation with the server, the
original user-specified rsize/wsize values were being modified directly.
This makes it impossible to recover these values after a connection
reset, leading to potential degraded performance after reconnection.

The other problem is that When negotiating read and write sizes, there are
cases where the negotiated values might calculate to zero, especially
during reconnection when server->max_read or server->max_write might be
reset. In general, these values come from the negotiation response.
According to MS-SMB2 specification, these values should be at least 65536
bytes.

This patch improves IO parameter handling:

1. Adds vol_rsize and vol_wsize fields to store the original user-specified
   values separately from the negotiated values
2. Uses got_rsize/got_wsize flags to determine if values were
   user-specified rather than checking for non-zero values, which is more
   reliable
3. Adds a prevent_zero_iosize() helper function to ensure IO sizes are
   never negotiated down to zero, which could happen in edge cases like
   when server->max_read/write is zero

The changes make the CIFS client more resilient to unusual server
responses and reconnection scenarios, preventing potential failures
when IO sizes are calculated to be zero.

Signed-off-by: Wang Zhaolong <wangzhaolong1@huawei.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/fs_context.c
fs/smb/client/fs_context.h
fs/smb/client/smb1ops.c
fs/smb/client/smb2ops.c
fs/smb/common/smb2pdu.h

index 9c3ded0cf006e5702f6171fd2885d63bc88b5509..ed543325c5183ac4e86ce045c9203c0ad304c0a4 100644 (file)
@@ -1333,6 +1333,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
        case Opt_rsize:
                ctx->rsize = result.uint_32;
                ctx->got_rsize = true;
+               ctx->vol_rsize = ctx->rsize;
                break;
        case Opt_wsize:
                ctx->wsize = result.uint_32;
@@ -1348,6 +1349,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
                                         ctx->wsize, PAGE_SIZE);
                        }
                }
+               ctx->vol_wsize = ctx->wsize;
                break;
        case Opt_acregmax:
                if (result.uint_32 > CIFS_MAX_ACTIMEO / HZ) {
index 42c6b66c2c1afc9521a7a93088a63364ef1abc40..23491401dac5ef5a12728f3c7fd0e9dd99f53539 100644 (file)
@@ -280,6 +280,9 @@ struct smb3_fs_context {
        bool use_client_guid:1;
        /* reuse existing guid for multichannel */
        u8 client_guid[SMB2_CLIENT_GUID_SIZE];
+       /* User-specified original r/wsize value */
+       unsigned int vol_rsize;
+       unsigned int vol_wsize;
        unsigned int bsize;
        unsigned int rasize;
        unsigned int rsize;
index 8701484805cdcb77f52c4f9577faca40bf8e0567..06b28da60a2d450b1960a575935c29f9c8a02618 100644 (file)
@@ -444,8 +444,8 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        unsigned int wsize;
 
        /* start with specified wsize, or default */
-       if (ctx->wsize)
-               wsize = ctx->wsize;
+       if (ctx->got_wsize)
+               wsize = ctx->vol_wsize;
        else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
                wsize = CIFS_DEFAULT_IOSIZE;
        else
@@ -497,7 +497,7 @@ cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        else
                defsize = server->maxBuf - sizeof(READ_RSP);
 
-       rsize = ctx->rsize ? ctx->rsize : defsize;
+       rsize = ctx->got_rsize ? ctx->vol_rsize : defsize;
 
        /*
         * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
index a700e592196149ab92a43bb4e3c9dd3f2284c04c..98643a546c680395328427c7b51c07f50343e052 100644 (file)
@@ -470,6 +470,17 @@ smb2_negotiate(const unsigned int xid,
        return rc;
 }
 
+static inline unsigned int
+prevent_zero_iosize(unsigned int size, const char *type)
+{
+       if (size == 0) {
+               cifs_dbg(VFS, "SMB: Zero %ssize calculated, using minimum value %u\n",
+                        type, CIFS_MIN_DEFAULT_IOSIZE);
+               return CIFS_MIN_DEFAULT_IOSIZE;
+       }
+       return size;
+}
+
 static unsigned int
 smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
 {
@@ -477,12 +488,12 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        unsigned int wsize;
 
        /* start with specified wsize, or default */
-       wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE;
+       wsize = ctx->got_wsize ? ctx->vol_wsize : CIFS_DEFAULT_IOSIZE;
        wsize = min_t(unsigned int, wsize, server->max_write);
        if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
                wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
 
-       return wsize;
+       return prevent_zero_iosize(wsize, "w");
 }
 
 static unsigned int
@@ -492,7 +503,7 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        unsigned int wsize;
 
        /* start with specified wsize, or default */
-       wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE;
+       wsize = ctx->got_wsize ? ctx->vol_wsize : SMB3_DEFAULT_IOSIZE;
        wsize = min_t(unsigned int, wsize, server->max_write);
 #ifdef CONFIG_CIFS_SMB_DIRECT
        if (server->rdma) {
@@ -514,7 +525,7 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
                wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
 
-       return wsize;
+       return prevent_zero_iosize(wsize, "w");
 }
 
 static unsigned int
@@ -524,13 +535,13 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        unsigned int rsize;
 
        /* start with specified rsize, or default */
-       rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE;
+       rsize = ctx->got_rsize ? ctx->vol_rsize : CIFS_DEFAULT_IOSIZE;
        rsize = min_t(unsigned int, rsize, server->max_read);
 
        if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
                rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
 
-       return rsize;
+       return prevent_zero_iosize(rsize, "r");
 }
 
 static unsigned int
@@ -540,7 +551,7 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        unsigned int rsize;
 
        /* start with specified rsize, or default */
-       rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE;
+       rsize = ctx->got_rsize ? ctx->vol_rsize : SMB3_DEFAULT_IOSIZE;
        rsize = min_t(unsigned int, rsize, server->max_read);
 #ifdef CONFIG_CIFS_SMB_DIRECT
        if (server->rdma) {
@@ -563,7 +574,7 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
                rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
 
-       return rsize;
+       return prevent_zero_iosize(rsize, "r");
 }
 
 /*
index c7a0efda440367c18f85c40638e34b4606b0ec9f..764dca80c15cb1af1e500845ae36087ebd011bce 100644 (file)
@@ -95,6 +95,9 @@
  */
 #define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024)
 
+/* According to MS-SMB2 specification The minimum recommended value is 65536.*/
+#define CIFS_MIN_DEFAULT_IOSIZE (65536)
+
 /*
  * SMB2 Header Definition
  *