]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
smb: client: ensure aligned IO sizes
authorPaulo Alcantara <pc@manguebit.com>
Wed, 30 Apr 2025 23:15:48 +0000 (20:15 -0300)
committerSteve French <stfrench@microsoft.com>
Thu, 1 May 2025 13:35:58 +0000 (08:35 -0500)
Make all IO sizes multiple of PAGE_SIZE, either negotiated by the
server or passed through rsize, wsize and bsize mount options, to
prevent from breaking DIO reads and writes against servers that
enforce alignment as specified in MS-FSA 2.1.5.3 and 2.1.5.4.

Cc: linux-cifs@vger.kernel.org
Reviewed-by: David Howells <dhowells@redhat.com>
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/connect.c
fs/smb/client/file.c
fs/smb/client/fs_context.c
fs/smb/client/fs_context.h
fs/smb/client/smb1ops.c
fs/smb/client/smb2pdu.c

index df976ce6aed92f9333638e013300c45b3d984f23..6bf04d9a5491387fed6fda02a832ee2bd3bb836c 100644 (file)
@@ -3753,28 +3753,7 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
                }
        }
 
-       /*
-        * Clamp the rsize/wsize mount arguments if they are too big for the server
-        * and set the rsize/wsize to the negotiated values if not passed in by
-        * the user on mount
-        */
-       if ((cifs_sb->ctx->wsize == 0) ||
-           (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) {
-               cifs_sb->ctx->wsize =
-                       round_down(server->ops->negotiate_wsize(tcon, ctx), PAGE_SIZE);
-               /*
-                * in the very unlikely event that the server sent a max write size under PAGE_SIZE,
-                * (which would get rounded down to 0) then reset wsize to absolute minimum eg 4096
-                */
-               if (cifs_sb->ctx->wsize == 0) {
-                       cifs_sb->ctx->wsize = PAGE_SIZE;
-                       cifs_dbg(VFS, "wsize too small, reset to minimum ie PAGE_SIZE, usually 4096\n");
-               }
-       }
-       if ((cifs_sb->ctx->rsize == 0) ||
-           (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx)))
-               cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx);
-
+       cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
        /*
         * The cookie is initialized from volume info returned above.
         * Inside cifs_fscache_get_super_cookie it checks
index 9e8f404b9e56aeb7f710bafd19946570e53b7d59..851b74f557c10e5f14f014923bbbdbd31f6cf064 100644 (file)
@@ -160,10 +160,8 @@ static int cifs_prepare_read(struct netfs_io_subrequest *subreq)
        server = cifs_pick_channel(tlink_tcon(req->cfile->tlink)->ses);
        rdata->server = server;
 
-       if (cifs_sb->ctx->rsize == 0)
-               cifs_sb->ctx->rsize =
-                       server->ops->negotiate_rsize(tlink_tcon(req->cfile->tlink),
-                                                    cifs_sb->ctx);
+       cifs_negotiate_rsize(server, cifs_sb->ctx,
+                            tlink_tcon(req->cfile->tlink));
 
        rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
                                           &size, &rdata->credits);
index 2980941b96679c4832dcc2b08b910334a0853ef2..a634a34d4086a049fe72e9093a092eaf6028c031 100644 (file)
@@ -1021,6 +1021,7 @@ static int smb3_reconfigure(struct fs_context *fc)
        struct dentry *root = fc->root;
        struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
        struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
+       unsigned int rsize = ctx->rsize, wsize = ctx->wsize;
        char *new_password = NULL, *new_password2 = NULL;
        bool need_recon = false;
        int rc;
@@ -1103,11 +1104,8 @@ static int smb3_reconfigure(struct fs_context *fc)
        STEAL_STRING(cifs_sb, ctx, iocharset);
 
        /* if rsize or wsize not passed in on remount, use previous values */
-       if (ctx->rsize == 0)
-               ctx->rsize = cifs_sb->ctx->rsize;
-       if (ctx->wsize == 0)
-               ctx->wsize = cifs_sb->ctx->wsize;
-
+       ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
+       ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
 
        smb3_cleanup_fs_context_contents(cifs_sb->ctx);
        rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
@@ -1312,7 +1310,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
                                __func__);
                        goto cifs_parse_mount_err;
                }
-               ctx->bsize = result.uint_32;
+               ctx->bsize = CIFS_ALIGN_BSIZE(fc, result.uint_32);
                ctx->got_bsize = true;
                break;
        case Opt_rasize:
@@ -1336,24 +1334,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
                ctx->rasize = result.uint_32;
                break;
        case Opt_rsize:
-               ctx->rsize = result.uint_32;
+               ctx->rsize = CIFS_ALIGN_RSIZE(fc, result.uint_32);
                ctx->got_rsize = true;
                ctx->vol_rsize = ctx->rsize;
                break;
        case Opt_wsize:
-               ctx->wsize = result.uint_32;
+               ctx->wsize = CIFS_ALIGN_WSIZE(fc, result.uint_32);
                ctx->got_wsize = true;
-               if (ctx->wsize % PAGE_SIZE != 0) {
-                       ctx->wsize = round_down(ctx->wsize, PAGE_SIZE);
-                       if (ctx->wsize == 0) {
-                               ctx->wsize = PAGE_SIZE;
-                               cifs_dbg(VFS, "wsize too small, reset to minimum %ld\n", PAGE_SIZE);
-                       } else {
-                               cifs_dbg(VFS,
-                                        "wsize rounded down to %d to multiple of PAGE_SIZE %ld\n",
-                                        ctx->wsize, PAGE_SIZE);
-                       }
-               }
                ctx->vol_wsize = ctx->wsize;
                break;
        case Opt_acregmax:
index d1d29249bcdb97ee4846342ee3ca440cdf08cfe9..9e83302ce4b80113193c85593c78da2e55b10c6c 100644 (file)
                cifs_dbg(VFS, fmt, ## __VA_ARGS__);     \
        } while (0)
 
+static inline size_t cifs_io_align(struct fs_context *fc,
+                                  const char *name, size_t size)
+{
+       if (!size || !IS_ALIGNED(size, PAGE_SIZE)) {
+               cifs_errorf(fc, "unaligned %s, making it a multiple of %lu bytes\n",
+                           name, PAGE_SIZE);
+               size = umax(round_down(size, PAGE_SIZE), PAGE_SIZE);
+       }
+       return size;
+}
+
+#define CIFS_ALIGN_WSIZE(_fc, _size) cifs_io_align(_fc, "wsize", _size)
+#define CIFS_ALIGN_RSIZE(_fc, _size) cifs_io_align(_fc, "rsize", _size)
+#define CIFS_ALIGN_BSIZE(_fc, _size) cifs_io_align(_fc, "bsize", _size)
+
 enum smb_version {
        Smb_1 = 1,
        Smb_20,
@@ -361,4 +376,36 @@ static inline void cifs_mount_unlock(void)
        mutex_unlock(&cifs_mount_mutex);
 }
 
+static inline void cifs_negotiate_rsize(struct TCP_Server_Info *server,
+                                       struct smb3_fs_context *ctx,
+                                       struct cifs_tcon *tcon)
+{
+       unsigned int size;
+
+       size = umax(server->ops->negotiate_rsize(tcon, ctx), PAGE_SIZE);
+       if (ctx->rsize)
+               size = umax(umin(ctx->rsize, size), PAGE_SIZE);
+       ctx->rsize = round_down(size, PAGE_SIZE);
+}
+
+static inline void cifs_negotiate_wsize(struct TCP_Server_Info *server,
+                                       struct smb3_fs_context *ctx,
+                                       struct cifs_tcon *tcon)
+{
+       unsigned int size;
+
+       size = umax(server->ops->negotiate_wsize(tcon, ctx), PAGE_SIZE);
+       if (ctx->wsize)
+               size = umax(umin(ctx->wsize, size), PAGE_SIZE);
+       ctx->wsize = round_down(size, PAGE_SIZE);
+}
+
+static inline void cifs_negotiate_iosize(struct TCP_Server_Info *server,
+                                        struct smb3_fs_context *ctx,
+                                        struct cifs_tcon *tcon)
+{
+       cifs_negotiate_rsize(server, ctx, tcon);
+       cifs_negotiate_wsize(server, ctx, tcon);
+}
+
 #endif
index ab26b9e9b0e70f2bcdb6471b8fb4da8726fc2074..b27a182629ece9eeb812376d0a1690c9d60803f0 100644 (file)
@@ -432,7 +432,7 @@ cifs_negotiate(const unsigned int xid,
 }
 
 static unsigned int
-cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
+smb1_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
 {
        __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
        struct TCP_Server_Info *server = tcon->ses->server;
@@ -467,7 +467,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
 }
 
 static unsigned int
-cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
+smb1_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
 {
        __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
        struct TCP_Server_Info *server = tcon->ses->server;
@@ -1342,8 +1342,8 @@ struct smb_version_operations smb1_operations = {
        .check_trans2 = cifs_check_trans2,
        .need_neg = cifs_need_neg,
        .negotiate = cifs_negotiate,
-       .negotiate_wsize = cifs_negotiate_wsize,
-       .negotiate_rsize = cifs_negotiate_rsize,
+       .negotiate_wsize = smb1_negotiate_wsize,
+       .negotiate_rsize = smb1_negotiate_rsize,
        .sess_setup = CIFS_SessSetup,
        .logoff = CIFSSMBLogoff,
        .tree_connect = CIFSTCon,
index a9a49555d43bf6824f28271345630e3b934e33e7..0b35816d551f7a1c51a1ba530b7fe84b24371c66 100644 (file)
@@ -4093,12 +4093,8 @@ static void cifs_renegotiate_iosize(struct TCP_Server_Info *server,
                return;
 
        spin_lock(&tcon->sb_list_lock);
-       list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) {
-               cifs_sb->ctx->rsize =
-                       server->ops->negotiate_rsize(tcon, cifs_sb->ctx);
-               cifs_sb->ctx->wsize =
-                       server->ops->negotiate_wsize(tcon, cifs_sb->ctx);
-       }
+       list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link)
+               cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
        spin_unlock(&tcon->sb_list_lock);
 }