mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
smb: client: ensure aligned IO sizes
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>
This commit is contained in:
parent
f122121796
commit
c59f7c9661
6 changed files with 62 additions and 55 deletions
|
@ -3753,28 +3753,7 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
|
||||||
* 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);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The cookie is initialized from volume info returned above.
|
* The cookie is initialized from volume info returned above.
|
||||||
* Inside cifs_fscache_get_super_cookie it checks
|
* Inside cifs_fscache_get_super_cookie it checks
|
||||||
|
|
|
@ -160,10 +160,8 @@ static int cifs_prepare_read(struct netfs_io_subrequest *subreq)
|
||||||
server = cifs_pick_channel(tlink_tcon(req->cfile->tlink)->ses);
|
server = cifs_pick_channel(tlink_tcon(req->cfile->tlink)->ses);
|
||||||
rdata->server = server;
|
rdata->server = server;
|
||||||
|
|
||||||
if (cifs_sb->ctx->rsize == 0)
|
cifs_negotiate_rsize(server, cifs_sb->ctx,
|
||||||
cifs_sb->ctx->rsize =
|
tlink_tcon(req->cfile->tlink));
|
||||||
server->ops->negotiate_rsize(tlink_tcon(req->cfile->tlink),
|
|
||||||
cifs_sb->ctx);
|
|
||||||
|
|
||||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
|
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
|
||||||
&size, &rdata->credits);
|
&size, &rdata->credits);
|
||||||
|
|
|
@ -1021,6 +1021,7 @@ static int smb3_reconfigure(struct fs_context *fc)
|
||||||
struct dentry *root = fc->root;
|
struct dentry *root = fc->root;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
|
||||||
struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
|
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;
|
char *new_password = NULL, *new_password2 = NULL;
|
||||||
bool need_recon = false;
|
bool need_recon = false;
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -1103,11 +1104,8 @@ static int smb3_reconfigure(struct fs_context *fc)
|
||||||
STEAL_STRING(cifs_sb, ctx, iocharset);
|
STEAL_STRING(cifs_sb, ctx, iocharset);
|
||||||
|
|
||||||
/* if rsize or wsize not passed in on remount, use previous values */
|
/* if rsize or wsize not passed in on remount, use previous values */
|
||||||
if (ctx->rsize == 0)
|
ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
|
||||||
ctx->rsize = cifs_sb->ctx->rsize;
|
ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
|
||||||
if (ctx->wsize == 0)
|
|
||||||
ctx->wsize = cifs_sb->ctx->wsize;
|
|
||||||
|
|
||||||
|
|
||||||
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
|
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
|
||||||
rc = smb3_fs_context_dup(cifs_sb->ctx, 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__);
|
__func__);
|
||||||
goto cifs_parse_mount_err;
|
goto cifs_parse_mount_err;
|
||||||
}
|
}
|
||||||
ctx->bsize = result.uint_32;
|
ctx->bsize = CIFS_ALIGN_BSIZE(fc, result.uint_32);
|
||||||
ctx->got_bsize = true;
|
ctx->got_bsize = true;
|
||||||
break;
|
break;
|
||||||
case Opt_rasize:
|
case Opt_rasize:
|
||||||
|
@ -1336,24 +1334,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
|
||||||
ctx->rasize = result.uint_32;
|
ctx->rasize = result.uint_32;
|
||||||
break;
|
break;
|
||||||
case Opt_rsize:
|
case Opt_rsize:
|
||||||
ctx->rsize = result.uint_32;
|
ctx->rsize = CIFS_ALIGN_RSIZE(fc, result.uint_32);
|
||||||
ctx->got_rsize = true;
|
ctx->got_rsize = true;
|
||||||
ctx->vol_rsize = ctx->rsize;
|
ctx->vol_rsize = ctx->rsize;
|
||||||
break;
|
break;
|
||||||
case Opt_wsize:
|
case Opt_wsize:
|
||||||
ctx->wsize = result.uint_32;
|
ctx->wsize = CIFS_ALIGN_WSIZE(fc, result.uint_32);
|
||||||
ctx->got_wsize = true;
|
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;
|
ctx->vol_wsize = ctx->wsize;
|
||||||
break;
|
break;
|
||||||
case Opt_acregmax:
|
case Opt_acregmax:
|
||||||
|
|
|
@ -20,6 +20,21 @@
|
||||||
cifs_dbg(VFS, fmt, ## __VA_ARGS__); \
|
cifs_dbg(VFS, fmt, ## __VA_ARGS__); \
|
||||||
} while (0)
|
} 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 {
|
enum smb_version {
|
||||||
Smb_1 = 1,
|
Smb_1 = 1,
|
||||||
Smb_20,
|
Smb_20,
|
||||||
|
@ -361,4 +376,36 @@ static inline void cifs_mount_unlock(void)
|
||||||
mutex_unlock(&cifs_mount_mutex);
|
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
|
#endif
|
||||||
|
|
|
@ -432,7 +432,7 @@ cifs_negotiate(const unsigned int xid,
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int
|
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);
|
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
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
|
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);
|
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
@ -1342,8 +1342,8 @@ struct smb_version_operations smb1_operations = {
|
||||||
.check_trans2 = cifs_check_trans2,
|
.check_trans2 = cifs_check_trans2,
|
||||||
.need_neg = cifs_need_neg,
|
.need_neg = cifs_need_neg,
|
||||||
.negotiate = cifs_negotiate,
|
.negotiate = cifs_negotiate,
|
||||||
.negotiate_wsize = cifs_negotiate_wsize,
|
.negotiate_wsize = smb1_negotiate_wsize,
|
||||||
.negotiate_rsize = cifs_negotiate_rsize,
|
.negotiate_rsize = smb1_negotiate_rsize,
|
||||||
.sess_setup = CIFS_SessSetup,
|
.sess_setup = CIFS_SessSetup,
|
||||||
.logoff = CIFSSMBLogoff,
|
.logoff = CIFSSMBLogoff,
|
||||||
.tree_connect = CIFSTCon,
|
.tree_connect = CIFSTCon,
|
||||||
|
|
|
@ -4093,12 +4093,8 @@ static void cifs_renegotiate_iosize(struct TCP_Server_Info *server,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spin_lock(&tcon->sb_list_lock);
|
spin_lock(&tcon->sb_list_lock);
|
||||||
list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) {
|
list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link)
|
||||||
cifs_sb->ctx->rsize =
|
cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
|
||||||
server->ops->negotiate_rsize(tcon, cifs_sb->ctx);
|
|
||||||
cifs_sb->ctx->wsize =
|
|
||||||
server->ops->negotiate_wsize(tcon, cifs_sb->ctx);
|
|
||||||
}
|
|
||||||
spin_unlock(&tcon->sb_list_lock);
|
spin_unlock(&tcon->sb_list_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue