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 df976ce..6bf04d9 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 9e8f404..851b74f 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 2980941..a634a34 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 d1d2924..9e83302 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 ab26b9e..b27a182 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 a9a4955..0b35816 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);
 }